[automerger] DO NOT MERGE Remove window obscurement information. am: edac95e737 am: dda3bdf8a8 am: 3cd049ca28 skipped: 2564b9fdf5 am: 67c9dace8b am: 09c59e7f69 am: a31fb53edd am: b63caaf102 skipped: c2116af15c

Change-Id: Ieec59681e3dc14340da368355ead622a7f0dcc1c
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..03af56d
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..cd05b21
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,15 @@
+ndk_headers {
+    name: "libandroid_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/**/*.h"],
+    license: "NOTICE",
+}
+
+subdirs = [
+    "cmds/*",
+    "libs/*",
+    "opengl",
+    "services/*",
+    "vulkan",
+]
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index 674c163..7e89220 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
 
 package android.view;
 
-parcelable Surface cpp_header "gui/Surface.h";
+parcelable Surface cpp_header "gui/view/Surface.h";
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
new file mode 100644
index 0000000..6c5869a
--- /dev/null
+++ b/cmds/atrace/Android.bp
@@ -0,0 +1,27 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_binary {
+    name: "atrace",
+    srcs: ["atrace.cpp"],
+
+    shared_libs: [
+        "libbinder",
+        "libhwbinder",
+        "android.hidl.manager@1.0",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libcutils",
+        "libutils",
+        "libz",
+        "libbase",
+    ],
+
+    init_rc: ["atrace.rc"],
+
+    product_variables: {
+        debuggable: {
+            init_rc: ["atrace_userdebug.rc"],
+        },
+    },
+}
diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk
deleted file mode 100644
index a787e95..0000000
--- a/cmds/atrace/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2012 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= atrace.cpp
-
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_MODULE:= atrace
-
-LOCAL_MODULE_TAGS:= optional
-
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    libcutils \
-    libutils \
-    libz \
-
-LOCAL_INIT_RC := atrace.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 5885738..47e04e7 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
- #define LOG_TAG "atrace"
+#define LOG_TAG "atrace"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -31,19 +31,26 @@
 #include <unistd.h>
 #include <zlib.h>
 
+#include <fstream>
+#include <memory>
+
 #include <binder/IBinder.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
 #include <cutils/properties.h>
 
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
 #include <utils/Trace.h>
+#include <android-base/file.h>
 
 using namespace android;
 
+using std::string;
 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 
 #define MAX_SYS_FILES 10
@@ -103,72 +110,85 @@
     { "ss",         "System Server",    ATRACE_TAG_SYSTEM_SERVER, { } },
     { "database",   "Database",         ATRACE_TAG_DATABASE, { } },
     { "network",    "Network",          ATRACE_TAG_NETWORK, { } },
+    { "adb",        "ADB",              ATRACE_TAG_ADB, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable" },
+        { REQ,      "events/sched/sched_switch/enable" },
+        { REQ,      "events/sched/sched_wakeup/enable" },
+        { OPT,      "events/sched/sched_waking/enable" },
+        { OPT,      "events/sched/sched_blocked_reason/enable" },
+        { OPT,      "events/sched/sched_cpu_hotplug/enable" },
     } },
     { "irq",        "IRQ Events",   0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/irq/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/ipi/enable" },
+        { REQ,      "events/irq/enable" },
+        { OPT,      "events/ipi/enable" },
+    } },
+    { "i2c",        "I2C Events",   0, {
+        { REQ,      "events/i2c/enable" },
+        { REQ,      "events/i2c/i2c_read/enable" },
+        { REQ,      "events/i2c/i2c_write/enable" },
+        { REQ,      "events/i2c/i2c_result/enable" },
+        { REQ,      "events/i2c/i2c_reply/enable" },
+        { OPT,      "events/i2c/smbus_read/enable" },
+        { OPT,      "events/i2c/smbus_write/enable" },
+        { OPT,      "events/i2c/smbus_result/enable" },
+        { OPT,      "events/i2c/smbus_reply/enable" },
     } },
     { "freq",       "CPU Frequency",    0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable" },
+        { REQ,      "events/power/cpu_frequency/enable" },
+        { OPT,      "events/power/clock_set_rate/enable" },
+        { OPT,      "events/power/cpu_frequency_limits/enable" },
     } },
     { "membus",     "Memory Bus Utilization", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/memory_bus/enable" },
+        { REQ,      "events/memory_bus/enable" },
     } },
     { "idle",       "CPU Idle",         0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
+        { REQ,      "events/power/cpu_idle/enable" },
     } },
     { "disk",       "Disk I/O",         0, {
-        { OPT,      "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
-        { OPT,      "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
+        { OPT,      "events/f2fs/f2fs_sync_file_enter/enable" },
+        { OPT,      "events/f2fs/f2fs_sync_file_exit/enable" },
+        { OPT,      "events/f2fs/f2fs_write_begin/enable" },
+        { OPT,      "events/f2fs/f2fs_write_end/enable" },
+        { OPT,      "events/ext4/ext4_da_write_begin/enable" },
+        { OPT,      "events/ext4/ext4_da_write_end/enable" },
+        { OPT,      "events/ext4/ext4_sync_file_enter/enable" },
+        { OPT,      "events/ext4/ext4_sync_file_exit/enable" },
+        { REQ,      "events/block/block_rq_issue/enable" },
+        { REQ,      "events/block/block_rq_complete/enable" },
     } },
     { "mmc",        "eMMC commands",    0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/mmc/enable" },
+        { REQ,      "events/mmc/enable" },
     } },
     { "load",       "CPU Load",         0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
+        { REQ,      "events/cpufreq_interactive/enable" },
     } },
     { "sync",       "Synchronization",  0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/sync/enable" },
+        { REQ,      "events/sync/enable" },
     } },
     { "workq",      "Kernel Workqueues", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/workqueue/enable" },
+        { REQ,      "events/workqueue/enable" },
     } },
     { "memreclaim", "Kernel Memory Reclaim", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable" },
+        { REQ,      "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
+        { REQ,      "events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
+        { REQ,      "events/vmscan/mm_vmscan_kswapd_wake/enable" },
+        { REQ,      "events/vmscan/mm_vmscan_kswapd_sleep/enable" },
     } },
     { "regulators",  "Voltage and Current Regulators", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/regulator/enable" },
+        { REQ,      "events/regulator/enable" },
     } },
     { "binder_driver", "Binder Kernel driver", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/binder/binder_transaction/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable" },
+        { REQ,      "events/binder/binder_transaction/enable" },
+        { REQ,      "events/binder/binder_transaction_received/enable" },
     } },
     { "binder_lock", "Binder global lock trace", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/binder/binder_lock/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/binder/binder_locked/enable" },
-        { REQ,      "/sys/kernel/debug/tracing/events/binder/binder_unlock/enable" },
+        { REQ,      "events/binder/binder_lock/enable" },
+        { REQ,      "events/binder/binder_locked/enable" },
+        { REQ,      "events/binder/binder_unlock/enable" },
     } },
     { "pagecache",  "Page cache", 0, {
-        { REQ,      "/sys/kernel/debug/tracing/events/filemap/enable" },
+        { REQ,      "events/filemap/enable" },
     } },
 };
 
@@ -187,61 +207,65 @@
 /* Global state */
 static bool g_traceAborted = false;
 static bool g_categoryEnables[NELEM(k_categories)] = {};
+static std::string g_traceFolder;
 
 /* Sys file paths */
 static const char* k_traceClockPath =
-    "/sys/kernel/debug/tracing/trace_clock";
+    "trace_clock";
 
 static const char* k_traceBufferSizePath =
-    "/sys/kernel/debug/tracing/buffer_size_kb";
+    "buffer_size_kb";
+
+static const char* k_traceCmdlineSizePath =
+    "saved_cmdlines_size";
 
 static const char* k_tracingOverwriteEnablePath =
-    "/sys/kernel/debug/tracing/options/overwrite";
+    "options/overwrite";
 
 static const char* k_currentTracerPath =
-    "/sys/kernel/debug/tracing/current_tracer";
+    "current_tracer";
 
 static const char* k_printTgidPath =
-    "/sys/kernel/debug/tracing/options/print-tgid";
+    "options/print-tgid";
 
 static const char* k_funcgraphAbsTimePath =
-    "/sys/kernel/debug/tracing/options/funcgraph-abstime";
+    "options/funcgraph-abstime";
 
 static const char* k_funcgraphCpuPath =
-    "/sys/kernel/debug/tracing/options/funcgraph-cpu";
+    "options/funcgraph-cpu";
 
 static const char* k_funcgraphProcPath =
-    "/sys/kernel/debug/tracing/options/funcgraph-proc";
+    "options/funcgraph-proc";
 
 static const char* k_funcgraphFlatPath =
-    "/sys/kernel/debug/tracing/options/funcgraph-flat";
+    "options/funcgraph-flat";
 
 static const char* k_funcgraphDurationPath =
-    "/sys/kernel/debug/tracing/options/funcgraph-duration";
+    "options/funcgraph-duration";
 
 static const char* k_ftraceFilterPath =
-    "/sys/kernel/debug/tracing/set_ftrace_filter";
+    "set_ftrace_filter";
 
 static const char* k_tracingOnPath =
-    "/sys/kernel/debug/tracing/tracing_on";
+    "tracing_on";
 
 static const char* k_tracePath =
-    "/sys/kernel/debug/tracing/trace";
+    "trace";
 
 static const char* k_traceStreamPath =
-    "/sys/kernel/debug/tracing/trace_pipe";
+    "trace_pipe";
 
 static const char* k_traceMarkerPath =
-    "/sys/kernel/debug/tracing/trace_marker";
+    "trace_marker";
 
 // Check whether a file exists.
 static bool fileExists(const char* filename) {
-    return access(filename, F_OK) != -1;
+    return access((g_traceFolder + filename).c_str(), F_OK) != -1;
 }
 
 // Check whether a file is writable.
 static bool fileIsWritable(const char* filename) {
-    return access(filename, W_OK) != -1;
+    return access((g_traceFolder + filename).c_str(), W_OK) != -1;
 }
 
 // Truncate a file.
@@ -250,9 +274,9 @@
     // This uses creat rather than truncate because some of the debug kernel
     // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
     // calls to truncate, but they are cleared by calls to creat.
-    int traceFD = creat(path, 0);
+    int traceFD = creat((g_traceFolder + path).c_str(), 0);
     if (traceFD == -1) {
-        fprintf(stderr, "error truncating %s: %s (%d)\n", path,
+        fprintf(stderr, "error truncating %s: %s (%d)\n", (g_traceFolder + path).c_str(),
             strerror(errno), errno);
         return false;
     }
@@ -264,9 +288,10 @@
 
 static bool _writeStr(const char* filename, const char* str, int flags)
 {
-    int fd = open(filename, flags);
+    std::string fullFilename = g_traceFolder + filename;
+    int fd = open(fullFilename.c_str(), flags);
     if (fd == -1) {
-        fprintf(stderr, "error opening %s: %s (%d)\n", filename,
+        fprintf(stderr, "error opening %s: %s (%d)\n", fullFilename.c_str(),
                 strerror(errno), errno);
         return false;
     }
@@ -274,7 +299,7 @@
     bool ok = true;
     ssize_t len = strlen(str);
     if (write(fd, str, len) != len) {
-        fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
+        fprintf(stderr, "error writing to %s: %s (%d)\n", fullFilename.c_str(),
                 strerror(errno), errno);
         ok = false;
     }
@@ -300,7 +325,7 @@
 {
   char buffer[128];
   int len = 0;
-  int fd = open(k_traceMarkerPath, O_WRONLY);
+  int fd = open((g_traceFolder + k_traceMarkerPath).c_str(), O_WRONLY);
   if (fd == -1) {
       fprintf(stderr, "error opening %s: %s (%d)\n", k_traceMarkerPath,
               strerror(errno), errno);
@@ -363,7 +388,7 @@
 // Check whether the category would be supported on the device if the user
 // were root.  This function assumes that root is able to write to any file
 // that exists.  It performs the same logic as isCategorySupported, but it
-// uses file existance rather than writability in the /sys/ file checks.
+// uses file existence rather than writability in the /sys/ file checks.
 static bool isCategorySupportedForRoot(const TracingCategory& category)
 {
     bool ok = category.tags != 0;
@@ -416,56 +441,40 @@
     return writeStr(k_traceBufferSizePath, str);
 }
 
-// Read the trace_clock sysfs file and return true if it matches the requested
-// value.  The trace_clock file format is:
-// local [global] counter uptime perf
-static bool isTraceClock(const char *mode)
+// Set the default size of cmdline hashtable
+static bool setCmdlineSize()
 {
-    int fd = open(k_traceClockPath, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "error opening %s: %s (%d)\n", k_traceClockPath,
-            strerror(errno), errno);
-        return false;
+    if (fileExists(k_traceCmdlineSizePath)) {
+        return writeStr(k_traceCmdlineSizePath, "8192");
     }
-
-    char buf[4097];
-    ssize_t n = read(fd, buf, 4096);
-    close(fd);
-    if (n == -1) {
-        fprintf(stderr, "error reading %s: %s (%d)\n", k_traceClockPath,
-            strerror(errno), errno);
-        return false;
-    }
-    buf[n] = '\0';
-
-    char *start = strchr(buf, '[');
-    if (start == NULL) {
-        return false;
-    }
-    start++;
-
-    char *end = strchr(start, ']');
-    if (end == NULL) {
-        return false;
-    }
-    *end = '\0';
-
-    return strcmp(mode, start) == 0;
+    return true;
 }
 
-// Enable or disable the kernel's use of the global clock.  Disabling the global
-// clock will result in the kernel using a per-CPU local clock.
+// Set the clock to the best available option while tracing. Use 'boot' if it's
+// available; otherwise, use 'mono'. If neither are available use 'global'.
 // Any write to the trace_clock sysfs file will reset the buffer, so only
 // update it if the requested value is not the current value.
-static bool setGlobalClockEnable(bool enable)
+static bool setClock()
 {
-    const char *clock = enable ? "global" : "local";
+    std::ifstream clockFile((g_traceFolder + k_traceClockPath).c_str(), O_RDONLY);
+    std::string clockStr((std::istreambuf_iterator<char>(clockFile)),
+        std::istreambuf_iterator<char>());
 
-    if (isTraceClock(clock)) {
-        return true;
+    std::string newClock;
+    if (clockStr.find("boot") != std::string::npos) {
+        newClock = "boot";
+    } else if (clockStr.find("mono") != std::string::npos) {
+        newClock = "mono";
+    } else {
+        newClock = "global";
     }
 
-    return writeStr(k_traceClockPath, clock);
+    size_t begin = clockStr.find("[") + 1;
+    size_t end = clockStr.find("]");
+    if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) {
+        return true;
+    }
+    return writeStr(k_traceClockPath, newClock.c_str());
 }
 
 static bool setPrintTgidEnableIfPresent(bool enable)
@@ -503,6 +512,54 @@
     return true;
 }
 
+// Poke all the HAL processes in the system to get them to re-read
+// their system properties.
+static void pokeHalServices()
+{
+    using ::android::hidl::base::V1_0::IBase;
+    using ::android::hidl::manager::V1_0::IServiceManager;
+    using ::android::hardware::hidl_string;
+    using ::android::hardware::Return;
+
+    sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
+
+    if (sm == nullptr) {
+        fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
+        return;
+    }
+
+    auto listRet = sm->list([&](const auto &interfaces) {
+        for (size_t i = 0; i < interfaces.size(); i++) {
+            string fqInstanceName = interfaces[i];
+            string::size_type n = fqInstanceName.find("/");
+            if (n == std::string::npos || interfaces[i].size() == n+1)
+                continue;
+            hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
+            hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
+            Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
+            if (!interfaceRet.isOk()) {
+                // ignore
+                continue;
+            }
+
+            sp<IBase> interface = interfaceRet;
+            if (interface == nullptr) {
+                // ignore
+                continue;
+            }
+
+            auto notifyRet = interface->notifySyspropsChanged();
+            if (!notifyRet.isOk()) {
+                // ignore
+            }
+        }
+    });
+    if (!listRet.isOk()) {
+        // TODO(b/34242478) fix this when we determine the correct ACL
+        //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
+    }
+}
+
 // Set the trace tags that userland tracing uses, and poke the running
 // processes to pick up the new value.
 static bool setTagsProperty(uint64_t tags)
@@ -533,11 +590,11 @@
 
 // Set the system property that indicates which apps should perform
 // application-level tracing.
-static bool setAppCmdlineProperty(const char* cmdline)
+static bool setAppCmdlineProperty(char* cmdline)
 {
     char buf[PROPERTY_KEY_MAX];
     int i = 0;
-    const char* start = cmdline;
+    char* start = cmdline;
     while (start != NULL) {
         if (i == MAX_PACKAGES) {
             fprintf(stderr, "error: only 16 packages could be traced at once\n");
@@ -587,24 +644,14 @@
 // kernel.
 static bool verifyKernelTraceFuncs(const char* funcs)
 {
-    int fd = open(k_ftraceFilterPath, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
+    std::string buf;
+    if (!android::base::ReadFileToString(g_traceFolder + k_ftraceFilterPath, &buf)) {
+         fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
             strerror(errno), errno);
-        return false;
+         return false;
     }
 
-    char buf[4097];
-    ssize_t n = read(fd, buf, 4096);
-    close(fd);
-    if (n == -1) {
-        fprintf(stderr, "error reading %s: %s (%d)\n", k_ftraceFilterPath,
-            strerror(errno), errno);
-        return false;
-    }
-
-    buf[n] = '\0';
-    String8 funcList = String8::format("\n%s", buf);
+    String8 funcList = String8::format("\n%s",buf.c_str());
 
     // Make sure that every function listed in funcs is in the list we just
     // read from the kernel, except for wildcard inputs.
@@ -624,7 +671,6 @@
         func = strtok(NULL, ",");
     }
     free(myFuncs);
-
     return ok;
 }
 
@@ -724,7 +770,9 @@
     ok &= setCategoriesEnableFromFile(g_categoriesFile);
     ok &= setTraceOverwriteEnable(g_traceOverwrite);
     ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
-    ok &= setGlobalClockEnable(true);
+    // TODO: Re-enable after stabilization
+    //ok &= setCmdlineSize();
+    ok &= setClock();
     ok &= setPrintTgidEnableIfPresent(true);
     ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
 
@@ -754,8 +802,9 @@
         }
         packageList += value;
     }
-    ok &= setAppCmdlineProperty(packageList.data());
+    ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= pokeBinderServices();
+    pokeHalServices();
 
     // Disable all the sysfs enables.  This is done as a separate loop from
     // the enables to allow the same enable to exist in multiple categories.
@@ -797,7 +846,6 @@
     // Set the options back to their defaults.
     setTraceOverwriteEnable(true);
     setTraceBufferSizeKB(1);
-    setGlobalClockEnable(false);
     setPrintTgidEnableIfPresent(false);
     setKernelTraceFuncs(NULL);
 }
@@ -819,7 +867,7 @@
 static void streamTrace()
 {
     char trace_data[4096];
-    int traceFD = open(k_traceStreamPath, O_RDWR);
+    int traceFD = open((g_traceFolder + k_traceStreamPath).c_str(), O_RDWR);
     if (traceFD == -1) {
         fprintf(stderr, "error opening %s: %s (%d)\n", k_traceStreamPath,
                 strerror(errno), errno);
@@ -844,7 +892,7 @@
 static void dumpTrace(int outFd)
 {
     ALOGI("Dumping trace");
-    int traceFD = open(k_tracePath, O_RDWR);
+    int traceFD = open((g_traceFolder + k_tracePath).c_str(), O_RDWR);
     if (traceFD == -1) {
         fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
                 strerror(errno), errno);
@@ -853,30 +901,34 @@
 
     if (g_compress) {
         z_stream zs;
-        uint8_t *in, *out;
-        int result, flush;
-
         memset(&zs, 0, sizeof(zs));
-        result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
+
+        int result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
         if (result != Z_OK) {
             fprintf(stderr, "error initializing zlib: %d\n", result);
             close(traceFD);
             return;
         }
 
-        const size_t bufSize = 64*1024;
-        in = (uint8_t*)malloc(bufSize);
-        out = (uint8_t*)malloc(bufSize);
-        flush = Z_NO_FLUSH;
+        constexpr size_t bufSize = 64*1024;
+        std::unique_ptr<uint8_t> in(new uint8_t[bufSize]);
+        std::unique_ptr<uint8_t> out(new uint8_t[bufSize]);
+        if (!in || !out) {
+            fprintf(stderr, "couldn't allocate buffers\n");
+            close(traceFD);
+            return;
+        }
 
-        zs.next_out = out;
+        int flush = Z_NO_FLUSH;
+
+        zs.next_out = reinterpret_cast<Bytef*>(out.get());
         zs.avail_out = bufSize;
 
         do {
 
             if (zs.avail_in == 0) {
                 // More input is needed.
-                result = read(traceFD, in, bufSize);
+                result = read(traceFD, in.get(), bufSize);
                 if (result < 0) {
                     fprintf(stderr, "error reading trace: %s (%d)\n",
                             strerror(errno), errno);
@@ -885,14 +937,14 @@
                 } else if (result == 0) {
                     flush = Z_FINISH;
                 } else {
-                    zs.next_in = in;
+                    zs.next_in = reinterpret_cast<Bytef*>(in.get());
                     zs.avail_in = result;
                 }
             }
 
             if (zs.avail_out == 0) {
                 // Need to write the output.
-                result = write(outFd, out, bufSize);
+                result = write(outFd, out.get(), bufSize);
                 if ((size_t)result < bufSize) {
                     fprintf(stderr, "error writing deflated trace: %s (%d)\n",
                             strerror(errno), errno);
@@ -900,7 +952,7 @@
                     zs.avail_out = bufSize; // skip the final write
                     break;
                 }
-                zs.next_out = out;
+                zs.next_out = reinterpret_cast<Bytef*>(out.get());
                 zs.avail_out = bufSize;
             }
 
@@ -912,7 +964,7 @@
 
         if (zs.avail_out < bufSize) {
             size_t bytes = bufSize - zs.avail_out;
-            result = write(outFd, out, bytes);
+            result = write(outFd, out.get(), bytes);
             if ((size_t)result < bytes) {
                 fprintf(stderr, "error writing deflated trace: %s (%d)\n",
                         strerror(errno), errno);
@@ -923,9 +975,6 @@
         if (result != Z_OK) {
             fprintf(stderr, "error cleaning up zlib: %d\n", result);
         }
-
-        free(in);
-        free(out);
     } else {
         ssize_t sent = 0;
         while ((sent = sendfile(outFd, traceFD, NULL, 64*1024*1024)) > 0);
@@ -981,9 +1030,9 @@
                     "  -k fname,...    trace the listed kernel functions\n"
                     "  -n              ignore signals\n"
                     "  -s N            sleep for N seconds before tracing [default 0]\n"
-                    "  -t N            trace for N seconds [defualt 5]\n"
+                    "  -t N            trace for N seconds [default 5]\n"
                     "  -z              compress the trace dump\n"
-                    "  --async_start   start circular trace and return immediatly\n"
+                    "  --async_start   start circular trace and return immediately\n"
                     "  --async_dump    dump the current contents of circular trace buffer\n"
                     "  --async_stop    stop tracing and dump the current contents of circular\n"
                     "                    trace buffer\n"
@@ -998,6 +1047,29 @@
             );
 }
 
+bool findTraceFiles()
+{
+    static const std::string debugfs_path = "/sys/kernel/debug/tracing/";
+    static const std::string tracefs_path = "/sys/kernel/tracing/";
+    static const std::string trace_file = "trace_marker";
+
+    bool tracefs = access((tracefs_path + trace_file).c_str(), F_OK) != -1;
+    bool debugfs = access((debugfs_path + trace_file).c_str(), F_OK) != -1;
+
+    if (!tracefs && !debugfs) {
+        fprintf(stderr, "Error: Did not find trace folder\n");
+        return false;
+    }
+
+    if (tracefs) {
+        g_traceFolder = tracefs_path;
+    } else {
+        g_traceFolder = debugfs_path;
+    }
+
+    return true;
+}
+
 int main(int argc, char **argv)
 {
     bool async = false;
@@ -1011,6 +1083,11 @@
         exit(0);
     }
 
+    if (!findTraceFiles()) {
+        fprintf(stderr, "No trace folder found\n");
+        exit(-1);
+    }
+
     for (;;) {
         int ret;
         int option_index = 0;
@@ -1158,7 +1235,7 @@
             fflush(stdout);
             int outFd = STDOUT_FILENO;
             if (g_outputFile) {
-                outFd = open(g_outputFile, O_WRONLY | O_CREAT);
+                outFd = open(g_outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
             }
             if (outFd == -1) {
                 printf("Failed to open '%s', err=%d", g_outputFile, errno);
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 747cc69..d538145 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -4,63 +4,135 @@
 
 # Allow writing to the kernel trace log.
     chmod 0222 /sys/kernel/debug/tracing/trace_marker
+    chmod 0222 /sys/kernel/tracing/trace_marker
 
 # Allow the shell group to enable (some) kernel tracing.
     chown root shell /sys/kernel/debug/tracing/trace_clock
+    chown root shell /sys/kernel/tracing/trace_clock
     chown root shell /sys/kernel/debug/tracing/buffer_size_kb
+    chown root shell /sys/kernel/tracing/buffer_size_kb
     chown root shell /sys/kernel/debug/tracing/options/overwrite
+    chown root shell /sys/kernel/tracing/options/overwrite
     chown root shell /sys/kernel/debug/tracing/options/print-tgid
+    chown root shell /sys/kernel/tracing/options/print-tgid
+    chown root shell /sys/kernel/debug/tracing/saved_cmdlines_size
+    chown root shell /sys/kernel/tracing/saved_cmdlines_size
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+    chown root shell /sys/kernel/tracing/events/sched/sched_switch/enable
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+    chown root shell /sys/kernel/tracing/events/sched/sched_wakeup/enable
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
+    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/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
+    chown root shell /sys/kernel/tracing/events/power/cpu_idle/enable
     chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
+    chown root shell /sys/kernel/tracing/events/power/clock_set_rate/enable
     chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
+    chown root shell /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
     chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+    chown root shell /sys/kernel/tracing/events/cpufreq_interactive/enable
     chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
     chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
     chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
     chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
+    chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
     chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
+    chown root shell /sys/kernel/tracing/events/binder/binder_transaction/enable
     chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
+    chown root shell /sys/kernel/tracing/events/binder/binder_transaction_received/enable
     chown root shell /sys/kernel/debug/tracing/events/binder/binder_lock/enable
+    chown root shell /sys/kernel/tracing/events/binder/binder_lock/enable
     chown root shell /sys/kernel/debug/tracing/events/binder/binder_locked/enable
+    chown root shell /sys/kernel/tracing/events/binder/binder_locked/enable
     chown root shell /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+    chown root shell /sys/kernel/tracing/events/binder/binder_unlock/enable
 
     chown root shell /sys/kernel/debug/tracing/tracing_on
+    chown root shell /sys/kernel/tracing/tracing_on
 
     chmod 0664 /sys/kernel/debug/tracing/trace_clock
+    chmod 0664 /sys/kernel/tracing/trace_clock
     chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb
+    chmod 0664 /sys/kernel/tracing/buffer_size_kb
     chmod 0664 /sys/kernel/debug/tracing/options/overwrite
+    chmod 0664 /sys/kernel/tracing/options/overwrite
     chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
+    chmod 0664 /sys/kernel/tracing/options/print-tgid
+    chmod 0664 /sys/kernel/debug/tracing/saved_cmdlines_size
+    chmod 0664 /sys/kernel/tracing/saved_cmdlines_size
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
+    chmod 0664 /sys/kernel/tracing/events/sched/sched_switch/enable
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
+    chmod 0664 /sys/kernel/tracing/events/sched/sched_wakeup/enable
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable
+    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/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
+    chmod 0664 /sys/kernel/tracing/events/power/cpu_idle/enable
     chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
+    chmod 0664 /sys/kernel/tracing/events/power/clock_set_rate/enable
     chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
+    chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
     chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+    chmod 0664 /sys/kernel/tracing/events/cpufreq_interactive/enable
     chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
     chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
     chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
     chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
+    chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
     chmod 0664 /sys/kernel/debug/tracing/tracing_on
+    chmod 0664 /sys/kernel/tracing/tracing_on
     chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
+    chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction/enable
     chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
+    chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction_received/enable
     chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
+    chmod 0664 /sys/kernel/tracing/events/binder/binder_lock/enable
     chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
+    chmod 0664 /sys/kernel/tracing/events/binder/binder_locked/enable
     chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+    chmod 0664 /sys/kernel/tracing/events/binder/binder_unlock/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_read/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_write/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_result/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/i2c_reply/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_read/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_write/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_result/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable
+    chmod 0664 /sys/kernel/tracing/events/i2c/smbus_reply/enable
 
     # Tracing disabled by default
     write /sys/kernel/debug/tracing/tracing_on 0
+    write /sys/kernel/tracing/tracing_on 0
 
 # Allow only the shell group to read and truncate the kernel trace.
     chown root shell /sys/kernel/debug/tracing/trace
+    chown root shell /sys/kernel/tracing/trace
     chmod 0660 /sys/kernel/debug/tracing/trace
+    chmod 0660 /sys/kernel/tracing/trace
 
 on property:persist.debug.atrace.boottrace=1
     start boottrace
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
new file mode 100644
index 0000000..5fd28e2
--- /dev/null
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -0,0 +1,47 @@
+## Permissions to allow additional system-wide tracing to the kernel trace buffer.
+## The default list of permissions is set in frameworks/native/cmds/atrace/atrace.rc
+
+# Allow the shell group to enable kernel tracepoints:
+
+on post-fs
+    chown root shell /sys/kernel/debug/tracing/events/sync/enable
+    chown root shell /sys/kernel/debug/tracing/events/workqueue/enable
+    chown root shell /sys/kernel/debug/tracing/events/regulator/enable
+    chown root shell /sys/kernel/debug/tracing/events/pagecache/enable
+
+    # irq
+    chown root shell /sys/kernel/debug/tracing/events/irq/enable
+    chown root shell /sys/kernel/debug/tracing/events/ipi/enable
+
+    # disk
+    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
+    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
+    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
+    chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
+    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
+    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
+    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
+    chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
+    chown root shell /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
+    chown root shell /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
+
+    chmod 0664 /sys/kernel/debug/tracing/events/sync/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/workqueue/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/regulator/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/pagecache/enable
+
+    # irq
+    chmod 0664 /sys/kernel/debug/tracing/events/irq/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/ipi/enable
+
+    # disk
+    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
diff --git a/cmds/bugreport/Android.bp b/cmds/bugreport/Android.bp
new file mode 100644
index 0000000..139e4b2
--- /dev/null
+++ b/cmds/bugreport/Android.bp
@@ -0,0 +1,6 @@
+cc_binary {
+    name: "bugreport",
+    srcs: ["bugreport.cpp"],
+    cflags: ["-Wall"],
+    shared_libs: ["libcutils"],
+}
diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk
deleted file mode 100644
index ced5d30..0000000
--- a/cmds/bugreport/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= bugreport.cpp
-
-LOCAL_MODULE:= bugreport
-
-LOCAL_CFLAGS := -Wall
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index 2697f09..eb0d898 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -17,3 +17,4 @@
 
 - `OK:<path_to_bugreport_file>` in case of success.
 - `FAIL:<error message>` in case of failure.
+
diff --git a/cmds/cmd/Android.mk b/cmds/cmd/Android.mk
index ac2f4c0..d565e57 100644
--- a/cmds/cmd/Android.mk
+++ b/cmds/cmd/Android.mk
@@ -7,8 +7,11 @@
 LOCAL_SHARED_LIBRARIES := \
 	libutils \
 	liblog \
+    libselinux \
 	libbinder
-	
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE)
 
 ifeq ($(TARGET_OS),linux)
 	LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index ed740d3..7e05d72 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -21,7 +21,10 @@
 #include <binder/ProcessState.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
 #include <binder/TextOutput.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 #include <utils/Vector.h>
 
 #include <getopt.h>
@@ -29,7 +32,16 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <sys/time.h>
+#include <errno.h>
+
+#include "selinux/selinux.h"
+#include "selinux/android.h"
+
+#include <UniquePtr.h>
+
+#define DEBUG 0
 
 using namespace android;
 
@@ -38,10 +50,72 @@
     return lhs->compare(*rhs);
 }
 
+struct SecurityContext_Delete {
+    void operator()(security_context_t p) const {
+        freecon(p);
+    }
+};
+typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+
+class MyShellCallback : public BnShellCallback
+{
+public:
+    bool mActive = true;
+
+    virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+        String8 path8(path);
+        char cwd[256];
+        getcwd(cwd, 256);
+        String8 fullPath(cwd);
+        fullPath.appendPath(path8);
+        if (!mActive) {
+            aerr << "Open attempt after active for: " << fullPath << endl;
+            return -EPERM;
+        }
+        int fd = open(fullPath.string(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG);
+        if (fd < 0) {
+            return fd;
+        }
+        if (is_selinux_enabled() && seLinuxContext.size() > 0) {
+            String8 seLinuxContext8(seLinuxContext);
+            security_context_t tmp = NULL;
+            int ret = getfilecon(fullPath.string(), &tmp);
+            Unique_SecurityContext context(tmp);
+            int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
+                    "file", "write", NULL);
+            if (accessGranted != 0) {
+                close(fd);
+                aerr << "System server has no access to file context " << context.get()
+                        << " (from path " << fullPath.string() << ", context "
+                        << seLinuxContext8.string() << ")" << endl;
+                return -EPERM;
+            }
+        }
+        return fd;
+    }
+};
+
 class MyResultReceiver : public BnResultReceiver
 {
 public:
-    virtual void send(int32_t /*resultCode*/) {
+    Mutex mMutex;
+    Condition mCondition;
+    bool mHaveResult = false;
+    int32_t mResult = 0;
+
+    virtual void send(int32_t resultCode) {
+        AutoMutex _l(mMutex);
+        mResult = resultCode;
+        mHaveResult = true;
+        mCondition.signal();
+    }
+
+    int32_t waitForResult() {
+        AutoMutex _l(mMutex);
+        while (!mHaveResult) {
+            mCondition.wait(mMutex);
+        }
+        return mResult;
     }
 };
 
@@ -49,18 +123,25 @@
 {
     signal(SIGPIPE, SIG_IGN);
     sp<ProcessState> proc = ProcessState::self();
+    // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
+    // not allowed to spawn any additional threads, but we still spawn
+    // a binder thread from userspace when we call startThreadPool().
+    // This is safe because we only have 2 callbacks, neither of which
+    // block.
+    // See b/36066697 for rationale
+    proc->setThreadPoolMaxThreadCount(0);
     proc->startThreadPool();
 
     sp<IServiceManager> sm = defaultServiceManager();
     fflush(stdout);
     if (sm == NULL) {
-        ALOGE("Unable to get default service manager!");
+        ALOGW("Unable to get default service manager!");
         aerr << "cmd: Unable to get default service manager!" << endl;
         return 20;
     }
 
     if (argc == 1) {
-        aout << "cmd: no service specified; use -l to list all services" << endl;
+        aerr << "cmd: No service specified; use -l to list all services" << endl;
         return 20;
     }
 
@@ -85,12 +166,41 @@
     String16 cmd = String16(argv[1]);
     sp<IBinder> service = sm->checkService(cmd);
     if (service == NULL) {
-        aerr << "Can't find service: " << argv[1] << endl;
+        ALOGW("Can't find service %s", argv[1]);
+        aerr << "cmd: Can't find service: " << argv[1] << endl;
         return 20;
     }
 
+    sp<MyShellCallback> cb = new MyShellCallback();
+    sp<MyResultReceiver> result = new MyResultReceiver();
+
+#if DEBUG
+    ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO,
+            STDERR_FILENO);
+#endif
+
     // TODO: block until a result is returned to MyResultReceiver.
-    IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
-            new MyResultReceiver());
-    return 0;
+    status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
+            cb, result);
+    if (err < 0) {
+        const char* errstr;
+        switch (err) {
+            case BAD_TYPE: errstr = "Bad type"; break;
+            case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
+            case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
+            case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
+            default: errstr = strerror(-err); break;
+        }
+        ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err);
+        aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " ("
+                << (-err) << ")" << endl;
+        return err;
+    }
+
+    cb->mActive = false;
+    status_t res = result->waitForResult();
+#if DEBUG
+    ALOGD("result=%d", (int)res);
+#endif
+    return res;
 }
diff --git a/cmds/dumpstate/.clang-format b/cmds/dumpstate/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpstate/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 791a7c4..a407ea2 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,24 +1,178 @@
 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 \
+        android.hidl.manager@1.0 \
+        libhidlbase \
+        libbase \
+        libbinder \
+        libcutils \
+        libdebuggerd_client \
+        libdumpstateaidl \
+        libdumpstateutil \
+        liblog \
+        libselinux \
+        libutils \
+        $(COMMON_ZIP_LIBRARIES)
+
+# ====================#
+# libdumpstateutil #
+# ====================#
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := libdumpstate_default.cpp
-LOCAL_MODULE := libdumpstate.default
+
+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)
 
-ifdef BOARD_WLAN_DEVICE
-LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
-endif
+LOCAL_MODULE := libdumpstateaidl
 
-LOCAL_SRC_FILES := dumpstate.cpp utils.cpp
+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 := libcutils liblog libselinux libbase
-# ZipArchive support, the order matters here to get all symbols.
-LOCAL_STATIC_LIBRARIES := libziparchive libz libmincrypt
-LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+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 #
+# =======================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test_fixture
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+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)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
new file mode 100644
index 0000000..0343277
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateInternal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+
+uint64_t Nanotime() {
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
+}
+
+// Switches to non-root user and group.
+bool DropRootUser() {
+    if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
+        MYLOGD("drop_root_user(): already running as Shell\n");
+        return true;
+    }
+    /* ensure we will keep capabilities when we drop root */
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+        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) {
+        MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+        return false;
+    }
+    if (setgid(AID_SHELL) != 0) {
+        MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+        return false;
+    }
+    if (setuid(AID_SHELL) != 0) {
+        MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+        return false;
+    }
+
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    capheader.pid = 0;
+
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[0].inheritable = 0;
+    capdata[1].inheritable = 0;
+
+    if (capset(&capheader, &capdata[0]) < 0) {
+        MYLOGE("capset failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+                       bool dry_run) {
+    const char* path = path_string.c_str();
+    if (!title.empty()) {
+        dprintf(out_fd, "------ %s (%s", title.c_str(), path);
+
+        struct stat st;
+        // Only show the modification time of non-device files.
+        size_t path_len = strlen(path);
+        if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
+            (path_len < 5 || memcmp(path, "/sys/", 5)) &&
+            (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
+            char stamp[80];
+            time_t mtime = st.st_mtime;
+            strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
+            dprintf(out_fd, ": %s", stamp);
+        }
+        dprintf(out_fd, ") ------\n");
+        fsync(out_fd);
+    }
+    if (dry_run) {
+        if (out_fd != STDOUT_FILENO) {
+            // There is no title, but we should still print a dry-run message
+            dprintf(out_fd, "%s: skipped on dry run\n", path);
+        } else if (!title.empty()) {
+            dprintf(out_fd, "\t(skipped on dry run)\n");
+        }
+        fsync(out_fd);
+        return 0;
+    }
+    bool newline = false;
+    fd_set read_set;
+    timeval tm;
+    while (true) {
+        FD_ZERO(&read_set);
+        FD_SET(fd, &read_set);
+        /* Timeout if no data is read for 30 seconds. */
+        tm.tv_sec = 30;
+        tm.tv_usec = 0;
+        uint64_t elapsed = Nanotime();
+        int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
+        if (ret == -1) {
+            dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
+            newline = true;
+            break;
+        } else if (ret == 0) {
+            elapsed = Nanotime() - elapsed;
+            dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+            newline = true;
+            break;
+        } else {
+            char buffer[65536];
+            ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
+            if (bytes_read > 0) {
+                android::base::WriteFully(out_fd, buffer, bytes_read);
+                newline = (buffer[bytes_read - 1] == '\n');
+            } else {
+                if (bytes_read == -1) {
+                    dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
+                    newline = true;
+                }
+                break;
+            }
+        }
+    }
+    close(fd);
+
+    if (!newline) dprintf(out_fd, "\n");
+    if (!title.empty()) dprintf(out_fd, "\n");
+    return 0;
+}
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
new file mode 100644
index 0000000..2f7704d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
+
+#include <cstdint>
+#include <string>
+
+// TODO: rename macros to DUMPSTATE_LOGXXX
+#ifndef MYLOGD
+#define MYLOGD(...)               \
+    fprintf(stderr, __VA_ARGS__); \
+    ALOGD(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGI
+#define MYLOGI(...)               \
+    fprintf(stderr, __VA_ARGS__); \
+    ALOGI(__VA_ARGS__);
+#endif
+
+#ifndef MYLOGE
+#define MYLOGE(...)               \
+    fprintf(stderr, __VA_ARGS__); \
+    ALOGE(__VA_ARGS__);
+#endif
+
+// Internal functions used by .cpp files on multiple build targets.
+// TODO: move to android::os::dumpstate::internal namespace
+
+// TODO: use functions from <chrono> instead
+const uint64_t NANOS_PER_SEC = 1000000000;
+uint64_t Nanotime();
+
+// Switches to non-root user and group.
+bool DropRootUser();
+
+// TODO: move to .cpp as static once is not used by utils.cpp anymore.
+int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
+                       bool dry_run = false);
+
+#endif  // FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
new file mode 100644
index 0000000..efe0466
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateService.h"
+
+#include <android-base/stringprintf.h>
+
+#include "android/os/BnDumpstate.h"
+
+#include "DumpstateInternal.h"
+
+namespace android {
+namespace os {
+
+namespace {
+class DumpstateToken : public BnDumpstateToken {};
+}
+
+DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+}
+
+char const* DumpstateService::getServiceName() {
+    return "dumpstate";
+}
+
+status_t DumpstateService::Start() {
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    status_t ret = BinderService<DumpstateService>::publish();
+    if (ret != android::OK) {
+        return ret;
+    }
+    sp<ProcessState> ps(ProcessState::self());
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    return android::OK;
+}
+
+binder::Status DumpstateService::setListener(const std::string& name,
+                                             const sp<IDumpstateListener>& listener,
+                                             sp<IDumpstateToken>* returned_token) {
+    *returned_token = nullptr;
+    if (name.empty()) {
+        MYLOGE("setListener(): name not set\n");
+        return binder::Status::ok();
+    }
+    if (listener == nullptr) {
+        MYLOGE("setListener(): listener not set\n");
+        return binder::Status::ok();
+    }
+    std::lock_guard<std::mutex> lock(lock_);
+    if (ds_.listener_ != nullptr) {
+        MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+        return binder::Status::ok();
+    }
+
+    ds_.listener_name_ = name;
+    ds_.listener_ = listener;
+    *returned_token = new DumpstateToken();
+
+    return binder::Status::ok();
+}
+
+status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+    dprintf(fd, "id: %d\n", ds_.id_);
+    dprintf(fd, "pid: %d\n", ds_.pid_);
+    dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "true" : "false");
+    dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
+    dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+    dprintf(fd, "progress:\n");
+    ds_.progress_->Dump(fd, "  ");
+    dprintf(fd, "args: %s\n", ds_.args_.c_str());
+    dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+    dprintf(fd, "version: %s\n", ds_.version_.c_str());
+    dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str());
+    dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
+    dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
+    dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
+    dprintf(fd, "path: %s\n", ds_.path_.c_str());
+    dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+    dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
+    dprintf(fd, "name: %s\n", ds_.name_.c_str());
+    dprintf(fd, "now: %ld\n", ds_.now_);
+    dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
+    dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
+    dprintf(fd, "notification title: %s\n", ds_.notification_title.c_str());
+    dprintf(fd, "notification description: %s\n", ds_.notification_description.c_str());
+
+    return NO_ERROR;
+}
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
new file mode 100644
index 0000000..4352d3d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATE_H_
+#define ANDROID_OS_DUMPSTATE_H_
+
+#include <mutex>
+#include <vector>
+
+#include <binder/BinderService.h>
+
+#include "android/os/BnDumpstate.h"
+#include "android/os/BnDumpstateToken.h"
+#include "dumpstate.h"
+
+namespace android {
+namespace os {
+
+class DumpstateService : public BinderService<DumpstateService>, public BnDumpstate {
+  public:
+    DumpstateService();
+
+    static status_t Start();
+    static char const* getServiceName();
+
+    status_t dump(int fd, const Vector<String16>& args) override;
+    binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+                               sp<IDumpstateToken>* returned_token) override;
+
+  private:
+    Dumpstate& ds_;
+    std::mutex lock_;
+};
+
+}  // namespace os
+}  // namespace android
+
+#endif  // ANDROID_OS_DUMPSTATE_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
new file mode 100644
index 0000000..26702c4
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateUtil.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+namespace {
+
+static constexpr const char* kSuPath = "/system/xbin/su";
+
+static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
+    sigset_t child_mask, old_mask;
+    sigemptyset(&child_mask);
+    sigaddset(&child_mask, SIGCHLD);
+
+    if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
+        printf("*** sigprocmask failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    timespec ts;
+    ts.tv_sec = timeout_seconds;
+    ts.tv_nsec = 0;
+    int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
+    int saved_errno = errno;
+    // Set the signals back the way they were.
+    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
+        printf("*** sigprocmask failed: %s\n", strerror(errno));
+        if (ret == 0) {
+            return false;
+        }
+    }
+    if (ret == -1) {
+        errno = saved_errno;
+        if (errno == EAGAIN) {
+            errno = ETIMEDOUT;
+        } else {
+            printf("*** sigtimedwait failed: %s\n", strerror(errno));
+        }
+        return false;
+    }
+
+    pid_t child_pid = waitpid(pid, status, WNOHANG);
+    if (child_pid != pid) {
+        if (child_pid != -1) {
+            printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+        } else {
+            printf("*** waitpid failed: %s\n", strerror(errno));
+        }
+        return false;
+    }
+    return true;
+}
+}  // unnamed namespace
+
+CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
+CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
+
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
+    values.always_ = true;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
+    values.account_mode_ = SU_ROOT;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
+    values.account_mode_ = DROP_ROOT;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
+    values.output_mode_ = REDIRECT_TO_STDERR;
+    return *this;
+}
+
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
+    const std::string& message) {
+    values.logging_message_ = message;
+    return *this;
+}
+
+CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
+    return CommandOptions(values);
+}
+
+CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
+    : timeout_(timeout),
+      always_(false),
+      account_mode_(DONT_DROP_ROOT),
+      output_mode_(NORMAL_OUTPUT),
+      logging_message_("") {
+}
+
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
+}
+
+int64_t CommandOptions::Timeout() const {
+    return values.timeout_;
+}
+
+bool CommandOptions::Always() const {
+    return values.always_;
+}
+
+PrivilegeMode CommandOptions::PrivilegeMode() const {
+    return values.account_mode_;
+}
+
+OutputMode CommandOptions::OutputMode() const {
+    return values.output_mode_;
+}
+
+std::string CommandOptions::LoggingMessage() const {
+    return values.logging_message_;
+}
+
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
+    return CommandOptions::CommandOptionsBuilder(timeout);
+}
+
+std::string PropertiesHelper::build_type_ = "";
+int PropertiesHelper::dry_run_ = -1;
+
+bool PropertiesHelper::IsUserBuild() {
+    if (build_type_.empty()) {
+        build_type_ = android::base::GetProperty("ro.build.type", "user");
+    }
+    return "user" == build_type_;
+}
+
+bool PropertiesHelper::IsDryRun() {
+    if (dry_run_ == -1) {
+        dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0;
+    }
+    return dry_run_ == 1;
+}
+
+int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
+    int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+    if (fd < 0) {
+        int err = errno;
+        if (title.empty()) {
+            dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+        } else {
+            dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
+                    strerror(err));
+        }
+        fsync(out_fd);
+        return -1;
+    }
+    return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
+}
+
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options) {
+    if (full_command.empty()) {
+        MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
+        return -1;
+    }
+
+    int size = full_command.size() + 1;  // null terminated
+    int starting_index = 0;
+    if (options.PrivilegeMode() == SU_ROOT) {
+        starting_index = 2;  // "su" "root"
+        size += starting_index;
+    }
+
+    std::vector<const char*> args;
+    args.resize(size);
+
+    std::string command_string;
+    if (options.PrivilegeMode() == SU_ROOT) {
+        args[0] = kSuPath;
+        command_string += kSuPath;
+        args[1] = "root";
+        command_string += " root ";
+    }
+    for (size_t i = 0; i < full_command.size(); i++) {
+        args[i + starting_index] = full_command[i].data();
+        command_string += args[i + starting_index];
+        if (i != full_command.size() - 1) {
+            command_string += " ";
+        }
+    }
+    args[size - 1] = nullptr;
+
+    const char* command = command_string.c_str();
+
+    if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
+        dprintf(fd, "Skipping '%s' on user build.\n", command);
+        return 0;
+    }
+
+    if (!title.empty()) {
+        dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
+        fsync(fd);
+    }
+
+    const std::string& logging_message = options.LoggingMessage();
+    if (!logging_message.empty()) {
+        MYLOGI(logging_message.c_str(), command_string.c_str());
+    }
+
+    bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
+    bool redirecting_to_fd = STDOUT_FILENO != fd;
+
+    if (PropertiesHelper::IsDryRun() && !options.Always()) {
+        if (!title.empty()) {
+            dprintf(fd, "\t(skipped on dry run)\n");
+        } else if (redirecting_to_fd) {
+            // There is no title, but we should still print a dry-run message
+            dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
+        }
+        fsync(fd);
+        return 0;
+    }
+
+    const char* path = args[0];
+
+    uint64_t start = Nanotime();
+    pid_t pid = fork();
+
+    /* handle error case */
+    if (pid < 0) {
+        if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
+        MYLOGE("*** fork: %s\n", strerror(errno));
+        return pid;
+    }
+
+    /* handle child case */
+    if (pid == 0) {
+        if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
+            if (!silent) {
+                dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
+                        strerror(errno));
+            }
+            MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
+            return -1;
+        }
+
+        if (silent) {
+            // Redirects stdout to stderr
+            TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
+        } else if (redirecting_to_fd) {
+            // Redirect stdout to fd
+            TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
+            close(fd);
+        }
+
+        /* make sure the child dies when dumpstate dies */
+        prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+        /* just ignore SIGPIPE, will go down with parent's */
+        struct sigaction sigact;
+        memset(&sigact, 0, sizeof(sigact));
+        sigact.sa_handler = SIG_IGN;
+        sigaction(SIGPIPE, &sigact, NULL);
+
+        execvp(path, (char**)args.data());
+        // execvp's result will be handled after waitpid_with_timeout() below, but
+        // if it failed, it's safer to exit dumpstate.
+        MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
+        // Must call _exit (instead of exit), otherwise it will corrupt the zip
+        // file.
+        _exit(EXIT_FAILURE);
+    }
+
+    /* handle parent case */
+    int status;
+    bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+    fsync(fd);
+
+    uint64_t elapsed = Nanotime() - start;
+    if (!ret) {
+        if (errno == ETIMEDOUT) {
+            if (!silent)
+                dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+                        static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+            MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
+                   static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+        } else {
+            if (!silent)
+                dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
+                        static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
+                   static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
+        }
+        kill(pid, SIGTERM);
+        if (!waitpid_with_timeout(pid, 5, nullptr)) {
+            kill(pid, SIGKILL);
+            if (!waitpid_with_timeout(pid, 5, nullptr)) {
+                if (!silent)
+                    dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
+                            command, pid);
+                MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
+            }
+        }
+        return -1;
+    }
+
+    if (WIFSIGNALED(status)) {
+        if (!silent)
+            dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+        MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+        status = WEXITSTATUS(status);
+        if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
+        MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
+    }
+
+    return status;
+}
+
+int GetPidByName(const std::string& ps_name) {
+    DIR* proc_dir;
+    struct dirent* ps;
+    unsigned int pid;
+    std::string cmdline;
+
+    if (!(proc_dir = opendir("/proc"))) {
+        MYLOGE("Can't open /proc\n");
+        return -1;
+    }
+
+    while ((ps = readdir(proc_dir))) {
+        if (!(pid = atoi(ps->d_name))) {
+            continue;
+        }
+        android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline);
+        if (cmdline.find(ps_name) == std::string::npos) {
+            continue;
+        } else {
+            closedir(proc_dir);
+            return pid;
+        }
+    }
+    MYLOGE("can't find the pid\n");
+    closedir(proc_dir);
+    return -1;
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
new file mode 100644
index 0000000..5a8ce5b
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_OS_DUMPSTATE_UTIL_H_
+#define ANDROID_OS_DUMPSTATE_UTIL_H_
+
+#include <cstdint>
+#include <string>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * Defines the Linux account that should be executing a command.
+ */
+enum PrivilegeMode {
+    /* Explicitly change the `uid` and `gid` to be `shell`.*/
+    DROP_ROOT,
+    /* Don't change the `uid` and `gid`. */
+    DONT_DROP_ROOT,
+    /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
+    SU_ROOT
+};
+
+/*
+ * Defines what should happen with the main output stream (`stdout` or fd) of a command.
+ */
+enum OutputMode {
+    /* Don't change main output. */
+    NORMAL_OUTPUT,
+    /* Redirect main output to `stderr`. */
+    REDIRECT_TO_STDERR
+};
+
+/*
+ * Value object used to set command options.
+ *
+ * Typically constructed using a builder with chained setters. Examples:
+ *
+ *  CommandOptions::WithTimeout(20).AsRoot().Build();
+ *  CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
+ *
+ * Although the builder could be used to dynamically set values. Example:
+ *
+ *  CommandOptions::CommandOptionsBuilder options =
+ *  CommandOptions::WithTimeout(10);
+ *  if (!is_user_build()) {
+ *    options.AsRoot();
+ *  }
+ *  RunCommand("command", {"args"}, options.Build());
+ */
+class CommandOptions {
+  private:
+    class CommandOptionsValues {
+      private:
+        CommandOptionsValues(int64_t timeout);
+
+        int64_t timeout_;
+        bool always_;
+        PrivilegeMode account_mode_;
+        OutputMode output_mode_;
+        std::string logging_message_;
+
+        friend class CommandOptions;
+        friend class CommandOptionsBuilder;
+    };
+
+    CommandOptions(const CommandOptionsValues& values);
+
+    const CommandOptionsValues values;
+
+  public:
+    class CommandOptionsBuilder {
+      public:
+        /* Sets the command to always run, even on `dry-run` mode. */
+        CommandOptionsBuilder& Always();
+        /* Sets the command's PrivilegeMode as `SU_ROOT` */
+        CommandOptionsBuilder& AsRoot();
+        /* Sets the command's PrivilegeMode as `DROP_ROOT` */
+        CommandOptionsBuilder& DropRoot();
+        /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
+        CommandOptionsBuilder& RedirectStderr();
+        /* When not empty, logs a message before executing the command.
+         * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
+        CommandOptionsBuilder& Log(const std::string& message);
+        /* Builds the command options. */
+        CommandOptions Build();
+
+      private:
+        CommandOptionsBuilder(int64_t timeout);
+        CommandOptionsValues values;
+        friend class CommandOptions;
+    };
+
+    /** Gets the command timeout, in seconds. */
+    int64_t Timeout() const;
+    /* Checks whether the command should always be run, even on dry-run mode. */
+    bool Always() const;
+    /** Gets the PrivilegeMode of the command. */
+    PrivilegeMode PrivilegeMode() const;
+    /** Gets the OutputMode of the command. */
+    OutputMode OutputMode() const;
+    /** Gets the logging message header, it any. */
+    std::string LoggingMessage() const;
+
+    /** Creates a builder with the requied timeout. */
+    static CommandOptionsBuilder WithTimeout(int64_t timeout);
+
+    // Common options.
+    static CommandOptions DEFAULT;
+    static CommandOptions AS_ROOT;
+};
+
+/*
+ * System properties helper.
+ */
+class PropertiesHelper {
+    friend class DumpstateBaseTest;
+
+  public:
+    /*
+     * Gets whether device is running a `user` build.
+     */
+    static bool IsUserBuild();
+
+    /*
+     * When running in dry-run mode, skips the real dumps and just print the section headers.
+     *
+     * Useful when debugging dumpstate or other bugreport-related activities.
+     *
+     * Dry-run mode is enabled by setting the system property `dumpstate.dry_run` to true.
+     */
+    static bool IsDryRun();
+
+  private:
+    static std::string build_type_;
+    static int dry_run_;
+};
+
+/*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |fd| file descriptor that receives the command's 'stdout'.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |full_command| array containing the command (first entry) and its arguments.
+ *                Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ */
+int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options = CommandOptions::DEFAULT);
+
+/*
+ * Dumps the contents of a file into a file descriptor.
+ *
+ * |fd| file descriptor where the file is dumped into.
+ * |title| description of the command printed on `stdout` (or empty to skip
+ * description).
+ * |path| location of the file to be dumped.
+ */
+int DumpFileToFd(int fd, const std::string& title, const std::string& path);
+
+/*
+ * Finds the process id by process name.
+ * |ps_name| the process name we want to search for
+ */
+int GetPidByName(const std::string& ps_name);
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+#endif  // ANDROID_OS_DUMPSTATE_UTIL_H_
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
new file mode 100644
index 0000000..0302ea5
--- /dev/null
+++ b/cmds/dumpstate/README.md
@@ -0,0 +1,103 @@
+# `dumpstate` development tips
+
+## To build `dumpstate`
+
+Do a full build first:
+
+```
+m -j dumpstate
+```
+
+Then incremental ones:
+
+```
+mmm -j frameworks/native/cmds/dumpstate
+```
+
+If you're working on device-specific code, you might need to build them as well. Example:
+
+```
+mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate
+```
+
+## To build, deploy, and take a bugreport
+
+```
+mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report
+```
+
+## To build, deploy, and run unit tests
+
+First create `/data/nativetest`:
+
+```
+adb shell mkdir /data/nativetest
+```
+
+Then run:
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test
+```
+
+And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
+
+```
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
+```
+
+## To take quick bugreports
+
+```
+adb shell setprop dumpstate.dry_run true
+```
+
+## To change the `dumpstate` version
+
+```
+adb shell setprop dumpstate.version VERSION_NAME
+```
+
+Example:
+
+```
+adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v
+```
+
+
+Then to restore the default version:
+
+```
+adb shell setprop dumpstate.version default
+```
+
+## Code style and formatting
+
+Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
+and make sure to run the following command prior to `repo upload`:
+
+```
+git clang-format --style=file HEAD~
+```
+
+## Useful Bash tricks
+
+```
+export BR_DIR=/bugreports
+
+alias br='adb shell cmd activity bug-report'
+alias ls_bugs='adb shell ls -l ${BR_DIR}/'
+
+unzip_bug() {
+  adb pull ${BR_DIR}/$1 && emacs $1 && mv $1 /tmp
+}
+
+less_bug() {
+  adb pull ${BR_DIR}/$1 && less $1 && mv $1 /tmp
+}
+
+rm_bugs() {
+ if [ -z "${BR_DIR}" ] ; then echo "Variable BR_DIR not set"; else adb shell rm -rf ${BR_DIR}/*; fi
+}
+
+```
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
new file mode 100644
index 0000000..4becccf
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
+
+/**
+  * Binder interface for the currently running dumpstate process.
+  * {@hide}
+  */
+interface IDumpstate {
+
+    /*
+     * Sets the listener for this dumpstate progress.
+     *
+     * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
+     * set (the listener behaves like a Highlander: There Can be Only One).
+     */
+    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
new file mode 100644
index 0000000..32717f4
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+  * Listener for dumpstate events.
+  *
+  * {@hide}
+  */
+interface IDumpstateListener {
+    void onProgressUpdated(int progress);
+    void onMaxProgressUpdated(int maxProgress);
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
new file mode 100644
index 0000000..7f74ceb
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+  * Token used by the IDumpstateListener to watch for dumpstate death.
+  * {@hide}
+  */
+interface IDumpstateToken {
+}
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index ca7d574..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -22,7 +22,7 @@
 file as the `ACTION_SEND_MULTIPLE` attachment.
 
 ## Version 1.0 (Android N)
-On _Android N (TBD)_, `dumpstate` generates a zip file directly (unless there
+On _Android N (Nougat)_, `dumpstate` generates a zip file directly (unless there
 is a failure, in which case it reverts to the flat file that is zipped by
 **Shell** and hence the end result is the _v0_ format).
 
@@ -55,6 +55,10 @@
 - `title.txt`: whose value is a single-line summary of the problem.
 - `description.txt`: whose value is a multi-line, detailed description of the problem.
 
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
 ## Intermediate versions
 During development, the versions will be suffixed with _-devX_ or
 _-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
@@ -63,8 +67,8 @@
 For example, the initial version during _Android N_ development was
 **1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were
 ready to parse that format, the version was named **1.0-dev2**,
-which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V 1.0-dev2` argument). Once that format became stable and tools
+which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property).
+Once that format became stable and tools
 knew how to parse it, the default version became **1.0-dev2**.
 
 Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0929d9b..f84d86d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "dumpstate"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -35,38 +37,37 @@
 #include <unistd.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <cutils/native_handle.h>
 #include <cutils/properties.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
 
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
-
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
 #include "dumpstate.h"
-#include "ScopedFd.h"
-#include "ziparchive/zip_writer.h"
 
-#include "mincrypt/sha256.h"
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
 
-using android::base::StringPrintf;
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::GetPidByName;
 
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
-// TODO: variables below should be part of dumpstate object
-static unsigned long id;
-static char build_type[PROPERTY_VALUE_MAX];
-static time_t now;
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
 static std::set<std::string> mount_points;
 void add_mountinfo();
-int control_socket_fd = -1;
-/* suffix of the bugreport files - it's typically the date (when invoked with -d),
- * although it could be changed by the user using a system property */
-static std::string suffix;
 
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
@@ -91,41 +92,57 @@
 
 static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
 
-const std::string ZIP_ROOT_DIR = "FS";
-std::string bugreport_dir;
-
-/*
- * List of supported zip format versions.
- *
- * See bugreport-format.txt for more info.
- */
-static std::string VERSION_DEFAULT = "1.0";
-
-bool is_user_build() {
-    return 0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1);
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+                      const CommandOptions& options = CommandOptions::DEFAULT) {
+    return ds.RunCommand(title, fullCommand, options);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+                       const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
+                       long dumpsysTimeout = 0) {
+    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
+}
+static int DumpFile(const std::string& title, const std::string& path) {
+    return ds.DumpFile(title, path);
 }
 
-/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
- * otherwise gets just those modified in the last half an hour. */
+// Relative directory (inside the zip) for all files copied as-is into the bugreport.
+static const std::string ZIP_ROOT_DIR = "FS";
+
+// Must be hardcoded because dumpstate HAL implementation need SELinux access to it
+static const std::string kDumpstateBoardPath = "/bugreports/dumpstate_board.txt";
+static const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt";
+
+static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
+static constexpr char PROPERTY_EXTRA_TITLE[] = "dumpstate.options.title";
+static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.description";
+
+static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
+
+/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
+ * otherwise, gets just those modified in the last half an hour. */
 static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
-    time_t thirty_minutes_ago = now - 60*30;
+    time_t thirty_minutes_ago = ds.now_ - 60 * 30;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
         snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
         int fd = TEMP_FAILURE_RETRY(open(data[i].name,
                                          O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
         struct stat st;
-        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
-            (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
-        data[i].fd = fd;
+        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
+            (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
+            data[i].fd = fd;
         } else {
-        close(fd);
+            close(fd);
             data[i].fd = -1;
         }
     }
 }
 
 // for_each_pid() callback to get mount info about a process.
-void do_mountinfo(int pid, const char *name) {
+void do_mountinfo(int pid, const char* name __attribute__((unused))) {
     char path[PATH_MAX];
 
     // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
@@ -142,7 +159,7 @@
     if (mount_points.find(linkname) == mount_points.end()) {
         // First time this mount point was found: add it
         snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
-        if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
+        if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
             mount_points.insert(linkname);
         } else {
             MYLOGE("Unable to add mountinfo %s to zip file\n", path);
@@ -151,12 +168,12 @@
 }
 
 void add_mountinfo() {
-    if (!is_zipping()) return;
-    const char *title = "MOUNT INFO";
+    if (!ds.IsZipping()) return;
+    std::string title = "MOUNT INFO";
     mount_points.clear();
-    DurationReporter duration_reporter(title, NULL);
-    for_each_pid(do_mountinfo, NULL);
-    MYLOGD("%s: %d entries added to zip file\n", title, (int) mount_points.size());
+    DurationReporter duration_reporter(title, true);
+    for_each_pid(do_mountinfo, nullptr);
+    MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
 }
 
 static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
@@ -175,40 +192,13 @@
             continue;
         }
         snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
-        dump_file(title, path);
+        DumpFile(title, path);
     }
 
     closedir(d);
 }
 
-// return pid of a userspace process. If not found or error, return 0.
-static unsigned int pid_of_process(const char* ps_name) {
-    DIR *proc_dir;
-    struct dirent *ps;
-    unsigned int pid;
-    std::string cmdline;
 
-    if (!(proc_dir = opendir("/proc"))) {
-        MYLOGE("Can't open /proc\n");
-        return 0;
-    }
-
-    while ((ps = readdir(proc_dir))) {
-        if (!(pid = atoi(ps->d_name))) {
-            continue;
-        }
-        android::base::ReadFileToString("/proc/"
-                + std::string(ps->d_name) + "/cmdline", &cmdline);
-        if (cmdline.find(ps_name) == std::string::npos) {
-            continue;
-        } else {
-            closedir(proc_dir);
-            return pid;
-        }
-    }
-    closedir(proc_dir);
-    return 0;
-}
 
 // dump anrd's trace and add to the zip file.
 // 1. check if anrd is running on this device.
@@ -225,13 +215,13 @@
     long long cur_size = 0;
     const char *trace_path = "/data/misc/anrd/";
 
-    if (!zip_writer) {
-        MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+    if (!ds.IsZipping()) {
+        MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
         return false;
     }
 
     // find anrd's pid if it is running.
-    pid = pid_of_process("/system/xbin/anrd");
+    pid = GetPidByName("/system/xbin/anrd");
 
     if (pid > 0) {
         if (stat(trace_path, &st) == 0) {
@@ -243,7 +233,8 @@
 
         // send SIGUSR1 to the anrd to generate a trace.
         sprintf(buf, "%u", pid);
-        if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+        if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
+                       CommandOptions::WithTimeout(1).Build())) {
             MYLOGE("anrd signal timed out. Please manually collect trace\n");
             return false;
         }
@@ -296,7 +287,7 @@
                 }
             }
             // Add to the zip file.
-            if (!add_zip_entry("anrd_trace.txt", path)) {
+            if (!ds.AddZipEntry("anrd_trace.txt", path)) {
                 MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
             } else {
                 if (remove(path)) {
@@ -312,11 +303,11 @@
 }
 
 static void dump_systrace() {
-    if (!is_zipping()) {
-        MYLOGD("Not dumping systrace because dumpstate is not zipping\n");
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
         return;
     }
-    std::string systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
+    std::string systrace_path = ds.GetPath("-systrace.txt");
     if (systrace_path.empty()) {
         MYLOGE("Not dumping systrace because path is empty\n");
         return;
@@ -333,17 +324,17 @@
 
     MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
             systrace_path.c_str());
-    if (run_command("SYSTRACE", 120, "/system/bin/atrace", "--async_dump", "-o",
-            systrace_path.c_str(), NULL)) {
+    if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
+                   CommandOptions::WithTimeout(120).Build())) {
         MYLOGE("systrace timed out, its zip entry will be incomplete\n");
-        // TODO: run_command tries to kill the process, but atrace doesn't die peacefully; ideally,
-        // we should call strace to stop itself, but there is no such option yet (just a
-        // --async_stop, which stops and dump
-        //        if (run_command("SYSTRACE", 10, "/system/bin/atrace", "--kill", NULL)) {
-        //            MYLOGE("could not stop systrace ");
-        //        }
+        // TODO: RunCommand tries to kill the process, but atrace doesn't die
+        // peacefully; ideally, we should call strace to stop itself, but there is no such option
+        // yet (just a --async_stop, which stops and dump
+        // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
+        //   MYLOGE("could not stop systrace ");
+        // }
     }
-    if (!add_zip_entry("systrace.txt", systrace_path)) {
+    if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
         MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
     } else {
         if (remove(systrace_path.c_str())) {
@@ -353,13 +344,13 @@
 }
 
 static void dump_raft() {
-    if (is_user_build()) {
+    if (PropertiesHelper::IsUserBuild()) {
         return;
     }
 
-    std::string raft_log_path = bugreport_dir + "/raft_log.txt";
-    if (raft_log_path.empty()) {
-        MYLOGD("raft_log_path is empty\n");
+    std::string raft_path = ds.GetPath("-raft_log.txt");
+    if (raft_path.empty()) {
+        MYLOGD("raft_path is empty\n");
         return;
     }
 
@@ -369,29 +360,30 @@
         return;
     }
 
-    if (!is_zipping()) {
-        // Write compressed and encoded raft logs to stdout if not zip_writer.
-        run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+    CommandOptions options = CommandOptions::WithTimeout(600).Build();
+    if (!ds.IsZipping()) {
+        // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
+        RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
         return;
     }
 
-    run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
-            "-o", raft_log_path.c_str(), NULL);
-    if (!add_zip_entry("raft_log.txt", raft_log_path)) {
-        MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+    RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
+    if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
+        MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
     } else {
-        if (remove(raft_log_path.c_str())) {
-            MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+        if (remove(raft_path.c_str())) {
+            MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
         }
     }
 }
 
 /**
- * Finds the last modified file in the directory dir whose name starts with file_prefix
+ * Finds the last modified file in the directory dir whose name starts with file_prefix.
+ *
  * Function returns empty string when it does not find a file
  */
-static std::string get_last_modified_file_matching_prefix(const std::string& dir,
-                                                          const std::string& file_prefix) {
+static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
+                                                 const std::string& file_prefix) {
     std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
     if (d == nullptr) {
         MYLOGD("Error %d opening %s\n", errno, dir.c_str());
@@ -400,7 +392,7 @@
 
     // Find the newest file matching the file_prefix in dir
     struct dirent *de;
-    time_t last_modified = 0;
+    time_t last_modified_time = 0;
     std::string last_modified_file = "";
     struct stat s;
 
@@ -412,39 +404,43 @@
         file = dir + "/" + file;
         int ret = stat(file.c_str(), &s);
 
-        if ((ret == 0) && (s.st_mtime > last_modified)) {
+        if ((ret == 0) && (s.st_mtime > last_modified_time)) {
             last_modified_file = file;
-            last_modified = s.st_mtime;
+            last_modified_time = s.st_mtime;
         }
     }
 
     return last_modified_file;
 }
 
-void dump_modem_logs() {
-    DurationReporter duration_reporter("dump_modem_logs");
-    if (is_user_build()) {
+static void DumpModemLogs() {
+    DurationReporter durationReporter("DUMP MODEM LOGS");
+    if (PropertiesHelper::IsUserBuild()) {
         return;
     }
 
-    if (!is_zipping()) {
+    if (!ds.IsZipping()) {
         MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
         return;
     }
 
-    char property[PROPERTY_VALUE_MAX];
-    property_get("ro.radio.log_prefix", property, "");
-    std::string file_prefix = std::string(property);
+    std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
+
     if(file_prefix.empty()) {
         MYLOGD("No modem log : file_prefix is empty\n");
         return;
     }
 
-    MYLOGD("dump_modem_logs: directory is %s and file_prefix is %s\n",
-           bugreport_dir.c_str(), file_prefix.c_str());
+    // TODO: b/33820081 we need to provide a right way to dump modem logs.
+    std::string radio_bugreport_dir = android::base::GetProperty("ro.radio.log_loc", "");
+    if (radio_bugreport_dir.empty()) {
+        radio_bugreport_dir = dirname(ds.GetPath("").c_str());
+    }
 
-    std::string modem_log_file =
-        get_last_modified_file_matching_prefix(bugreport_dir, file_prefix);
+    MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
+           radio_bugreport_dir.c_str(), file_prefix.c_str());
+
+    std::string modem_log_file = GetLastModifiedFileWithPrefix(radio_bugreport_dir, file_prefix);
 
     struct stat s;
     if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
@@ -453,7 +449,7 @@
     }
 
     std::string filename = basename(modem_log_file.c_str());
-    if (!add_zip_entry(filename, modem_log_file)) {
+    if (!ds.AddZipEntry(filename, modem_log_file)) {
         MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
     } else {
         MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
@@ -472,7 +468,7 @@
     return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
 }
 
-static bool skip_none(const char *path) {
+static bool skip_none(const char* path __attribute__((unused))) {
     return false;
 }
 
@@ -631,11 +627,10 @@
                                  / fields[__STAT_IO_TICKS];
 
         if (!write_perf && !write_ios) {
-            printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
-                   path, read_perf, read_ios, queue);
+            printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", path, read_perf, read_ios, queue);
         } else {
-            printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
-                   path, read_perf, read_ios, write_perf, write_ios, queue);
+            printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n", path, read_perf,
+                   read_ios, write_perf, write_ios, queue);
         }
 
         /* bugreport timeout factor adjustment */
@@ -646,132 +641,43 @@
     return 0;
 }
 
-/* Copied policy from system/core/logd/LogBuffer.cpp */
-
-#define LOG_BUFFER_SIZE (256 * 1024)
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-
-static bool valid_size(unsigned long value) {
-    if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
-        return false;
-    }
-
-    long pages = sysconf(_SC_PHYS_PAGES);
-    if (pages < 1) {
-        return true;
-    }
-
-    long pagesize = sysconf(_SC_PAGESIZE);
-    if (pagesize <= 1) {
-        pagesize = PAGE_SIZE;
-    }
-
-    // maximum memory impact a somewhat arbitrary ~3%
-    pages = (pages + 31) / 32;
-    unsigned long maximum = pages * pagesize;
-
-    if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
-        return true;
-    }
-
-    return value <= maximum;
-}
-
-static unsigned long property_get_size(const char *key) {
-    unsigned long value;
-    char *cp, property[PROPERTY_VALUE_MAX];
-
-    property_get(key, property, "");
-    value = strtoul(property, &cp, 10);
-
-    switch(*cp) {
-    case 'm':
-    case 'M':
-        value *= 1024;
-    /* FALLTHRU */
-    case 'k':
-    case 'K':
-        value *= 1024;
-    /* FALLTHRU */
-    case '\0':
-        break;
-
-    default:
-        value = 0;
-    }
-
-    if (!valid_size(value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
 /* timeout in ms */
 static unsigned long logcat_timeout(const char *name) {
-    static const char global_tuneable[] = "persist.logd.size"; // Settings App
-    static const char global_default[] = "ro.logd.size";       // BoardConfig.mk
-    char key[PROP_NAME_MAX];
-    unsigned long property_size, default_size;
-
-    default_size = property_get_size(global_tuneable);
-    if (!default_size) {
-        default_size = property_get_size(global_default);
-    }
-
-    snprintf(key, sizeof(key), "%s.%s", global_tuneable, name);
-    property_size = property_get_size(key);
-
-    if (!property_size) {
-        snprintf(key, sizeof(key), "%s.%s", global_default, name);
-        property_size = property_get_size(key);
-    }
-
-    if (!property_size) {
-        property_size = default_size;
-    }
-
-    if (!property_size) {
-        property_size = LOG_BUFFER_SIZE;
-    }
-
+    log_id_t id = android_name_to_log_id(name);
+    unsigned long property_size = __android_logger_get_buffer_size(id);
     /* Engineering margin is ten-fold our guess */
     return 10 * (property_size + worst_write_perf) / worst_write_perf;
 }
 
-/* End copy from system/core/logd/LogBuffer.cpp */
+void Dumpstate::PrintHeader() const {
+    std::string build, fingerprint, radio, bootloader, network;
+    char date[80];
 
-/* dumps the current system state to stdout */
-static void print_header(std::string version) {
-    char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
-    char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
-    char network[PROPERTY_VALUE_MAX], date[80];
-
-    property_get("ro.build.display.id", build, "(unknown)");
-    property_get("ro.build.fingerprint", fingerprint, "(unknown)");
-    property_get("ro.build.type", build_type, "(unknown)");
-    property_get("gsm.version.baseband", radio, "(unknown)");
-    property_get("ro.bootloader", bootloader, "(unknown)");
-    property_get("gsm.operator.alpha", network, "(unknown)");
-    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
+    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
+    fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
+    radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
+    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
+    network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
 
     printf("========================================================\n");
     printf("== dumpstate: %s\n", date);
     printf("========================================================\n");
 
     printf("\n");
-    printf("Build: %s\n", build);
-    printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
-    printf("Bootloader: %s\n", bootloader);
-    printf("Radio: %s\n", radio);
-    printf("Network: %s\n", network);
+    printf("Build: %s\n", build.c_str());
+    // NOTE: fingerprint entry format is important for other tools.
+    printf("Build fingerprint: '%s'\n", fingerprint.c_str());
+    printf("Bootloader: %s\n", bootloader.c_str());
+    printf("Radio: %s\n", radio.c_str());
+    printf("Network: %s\n", network.c_str());
 
     printf("Kernel: ");
-    dump_file(NULL, "/proc/version");
+    DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
-    printf("Bugreport format version: %s\n", version.c_str());
-    printf("Dumpstate info: id=%lu pid=%d\n", id, getpid());
+    printf("Bugreport format version: %s\n", version_.c_str());
+    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
+           PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
     printf("\n");
 }
 
@@ -783,10 +689,10 @@
       ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
 };
 
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
-    if (!is_zipping()) {
-        MYLOGD("Not adding entry %s from fd because dumpstate is not zipping\n",
-                entry_name.c_str());
+bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
+               entry_name.c_str());
         return false;
     }
     std::string valid_name = entry_name;
@@ -804,250 +710,305 @@
 
     // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
     // MYLOGD("Adding zip entry %s\n", entry_name.c_str());
-    int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
-            ZipWriter::kCompress, get_mtime(fd, now));
-    if (err) {
-        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+                                                  get_mtime(fd, ds.now_));
+    if (err != 0) {
+        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
     std::vector<uint8_t> buffer(65536);
     while (1) {
-        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), sizeof(buffer)));
+        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
         if (bytes_read == 0) {
             break;
         } else if (bytes_read == -1) {
             MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
             return false;
         }
-        err = zip_writer->WriteBytes(buffer.data(), bytes_read);
+        err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
         if (err) {
-            MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
+            MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
             return false;
         }
     }
 
-    err = zip_writer->FinishEntry();
-    if (err) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->FinishEntry();
+    if (err != 0) {
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
     return true;
 }
 
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
-    ScopedFd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
-    if (fd.get() == -1) {
+bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
+    android::base::unique_fd fd(
+        TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+    if (fd == -1) {
         MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
         return false;
     }
 
-    return add_zip_entry_from_fd(entry_name, fd.get());
+    return AddZipEntryFromFd(entry_name, fd.get());
 }
 
 /* adds a file to the existing zipped bugreport */
-static int _add_file_from_fd(const char *title, const char *path, int fd) {
-    return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
+static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
+    return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
 }
 
-// TODO: move to util.cpp
-void add_dir(const char *dir, bool recursive) {
-    if (!is_zipping()) {
-        MYLOGD("Not adding dir %s because dumpstate is not zipping\n", dir);
+void Dumpstate::AddDir(const std::string& dir, bool recursive) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
         return;
     }
-    MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive);
-    DurationReporter duration_reporter(dir, NULL);
-    dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
+    MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
+    DurationReporter duration_reporter(dir, true);
+    dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
 }
 
-bool is_zipping() {
-    return zip_writer != nullptr;
-}
-
-/* adds a text entry entry to the existing zip file. */
-static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
-    if (!is_zipping()) {
-        MYLOGD("Not adding text entry %s because dumpstate is not zipping\n", entry_name.c_str());
+bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
+               entry_name.c_str());
         return false;
     }
     MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
-    int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
-    if (err) {
-        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+    if (err != 0) {
+        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    err = zip_writer->WriteBytes(content.c_str(), content.length());
-    if (err) {
-        MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->WriteBytes(content.c_str(), content.length());
+    if (err != 0) {
+        MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    err = zip_writer->FinishEntry();
-    if (err) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->FinishEntry();
+    if (err != 0) {
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
     return true;
 }
 
-static void dump_iptables() {
-    run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
-    run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
-    run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
-    /* no ip6 nat */
-    run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
-    run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
-    run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
-    run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
-}
-
-static void do_kmsg() {
+static void DoKmsg() {
     struct stat st;
     if (!stat(PSTORE_LAST_KMSG, &st)) {
         /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
-        dump_file("LAST KMSG", PSTORE_LAST_KMSG);
+        DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
     } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
-        dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
+        DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
     } else {
         /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
-        dump_file("LAST KMSG", "/proc/last_kmsg");
+        DumpFile("LAST KMSG", "/proc/last_kmsg");
     }
 }
 
-static void do_logcat() {
+static void DoLogcat() {
     unsigned long timeout;
-    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+    // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
     // calculate timeout
     timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime",
-                                                        "-v", "printable",
-                                                        "-d",
-                                                        "*:v", NULL);
+    RunCommand("SYSTEM LOG",
+               {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid",
+                        "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
     timeout = logcat_timeout("events");
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events",
-                                                       "-v", "threadtime",
-                                                       "-v", "printable",
-                                                       "-d",
-                                                       "*:v", NULL);
+    RunCommand("EVENT LOG",
+               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
+                        "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
     timeout = logcat_timeout("radio");
     if (timeout < 20000) {
         timeout = 20000;
     }
-    run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio",
-                                                       "-v", "threadtime",
-                                                       "-v", "printable",
-                                                       "-d",
-                                                       "*:v", NULL);
+    RunCommand("RADIO LOG",
+               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
+                        "-d", "*:v"},
+               CommandOptions::WithTimeout(timeout / 1000).Build());
 
-    run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
+    RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
-    run_command("LAST LOGCAT", 10, "logcat", "-L",
-                                             "-b", "all",
-                                             "-v", "threadtime",
-                                             "-v", "printable",
-                                             "-d",
-                                             "*:v", NULL);
+    RunCommand("LAST LOGCAT",
+                {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
+                        "-d", "*:v"});
 }
 
-static void dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void DumpIpTables() {
+    RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+    RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+    RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
+    /* no ip6 nat */
+    RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+    RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
+}
+
+static void AddAnrTraceFiles() {
+    bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+    std::string dump_traces_dir;
+
+    /* show the traces we collected in main(), if that was done */
+    if (dump_traces_path != nullptr) {
+        if (add_to_zip) {
+            dump_traces_dir = dirname(dump_traces_path);
+            MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+            ds.AddDir(dump_traces_dir, true);
+        } else {
+            MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+                   dump_traces_path);
+            ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+        }
+    }
+
+    std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+    // Make sure directory is not added twice.
+    // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+    // generated by dump_traces() -  and anr_traces_path - which is retrieved from a system
+    // property - but in reality they're the same path (although the former could be nullptr).
+    // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+    // be revisited.
+    bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+    MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+           dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+    if (anr_traces_path.empty()) {
+        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+    } else {
+        int fd = TEMP_FAILURE_RETRY(
+            open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+        if (fd < 0) {
+            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+                   strerror(errno));
+        } else {
+            if (add_to_zip) {
+                if (!already_dumped) {
+                    MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+                           anr_traces_dir.c_str());
+                    ds.AddDir(anr_traces_dir, true);
+                    already_dumped = true;
+                }
+            } else {
+                MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+                       anr_traces_path.c_str());
+                dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+            }
+        }
+    }
+
+    if (add_to_zip && already_dumped) {
+        MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+        return;
+    }
+
+    /* slow traces for slow operations */
+    struct stat st;
+    if (!anr_traces_path.empty()) {
+        int tail = anr_traces_path.size() - 1;
+        while (tail > 0 && anr_traces_path.at(tail) != '/') {
+            tail--;
+        }
+        int i = 0;
+        while (1) {
+            anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+                              android::base::StringPrintf("slow%02d.txt", i);
+            if (stat(anr_traces_path.c_str(), &st)) {
+                // No traces file at this index, done with the files.
+                break;
+            }
+            ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+            i++;
+        }
+    }
+}
+
+static void dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
     dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
-    run_command("UPTIME", 10, "uptime", NULL);
+    RunCommand("UPTIME", {"uptime"});
     dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
     dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
-    dump_file("MEMORY INFO", "/proc/meminfo");
-    run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-H", NULL);
-    run_command("PROCRANK", 20, SU_PATH, "root", "procrank", NULL);
-    dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
-    dump_file("VMALLOC INFO", "/proc/vmallocinfo");
-    dump_file("SLAB INFO", "/proc/slabinfo");
-    dump_file("ZONEINFO", "/proc/zoneinfo");
-    dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
-    dump_file("BUDDYINFO", "/proc/buddyinfo");
-    dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
+    DumpFile("MEMORY INFO", "/proc/meminfo");
+    RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
+                            "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
+    RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
+    DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
+    DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
+    DumpFile("SLAB INFO", "/proc/slabinfo");
+    DumpFile("ZONEINFO", "/proc/zoneinfo");
+    DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
+    DumpFile("BUDDYINFO", "/proc/buddyinfo");
+    DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
 
-    dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
-    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-    dump_file("KERNEL SYNC", "/d/sync");
+    DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
+    DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+    DumpFile("KERNEL SYNC", "/d/sync");
 
-    run_command("PROCESSES AND THREADS", 10, "ps", "-Z", "-t", "-p", "-P", NULL);
-    run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
+    RunCommand("PROCESSES AND THREADS",
+               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
+    RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
 
-    run_command("PRINTENV", 10, "printenv", NULL);
-    run_command("NETSTAT", 10, "netstat", "-n", NULL);
-    run_command("LSMOD", 10, "lsmod", NULL);
+    if (ds.IsZipping()) {
+        RunCommand(
+                "HARDWARE HALS",
+                {"lshal", std::string("--debug=") + kLsHalDebugPath},
+                CommandOptions::AS_ROOT);
+
+        ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
+
+        unlink(kLsHalDebugPath.c_str());
+    } else {
+        RunCommand(
+                "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT);
+    }
+
+    RunCommand("PRINTENV", {"printenv"});
+    RunCommand("NETSTAT", {"netstat", "-nW"});
+    struct stat s;
+    if (stat("/proc/modules", &s) != 0) {
+        MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
+    } else {
+        RunCommand("LSMOD", {"lsmod"});
+    }
 
     do_dmesg();
 
-    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+    RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
     /* Dump Bluetooth HCI logs */
-    add_dir("/data/misc/bluetooth/logs", true);
+    ds.AddDir("/data/misc/bluetooth/logs", true);
 
-    if (!screenshot_path.empty()) {
+    if (!ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
-        take_screenshot(screenshot_path);
-        MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+        ds.TakeScreenshot();
     }
 
-    do_logcat();
+    DoLogcat();
 
-    /* show the traces we collected in main(), if that was done */
-    if (dump_traces_path != NULL) {
-        dump_file("VM TRACES JUST NOW", dump_traces_path);
-    }
-
-    /* only show ANR traces if they're less than 15 minutes old */
-    struct stat st;
-    char anr_traces_path[PATH_MAX];
-    property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
-    if (!anr_traces_path[0]) {
-        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
-    } else {
-      int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
-                                       O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-      if (fd < 0) {
-          printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
-      } else {
-          dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
-      }
-    }
-
-    /* slow traces for slow operations */
-    if (anr_traces_path[0] != 0) {
-        int tail = strlen(anr_traces_path)-1;
-        while (tail > 0 && anr_traces_path[tail] != '/') {
-            tail--;
-        }
-        int i = 0;
-        while (1) {
-            sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
-            if (stat(anr_traces_path, &st)) {
-                // No traces file at this index, done with the files.
-                break;
-            }
-            dump_file("VM TRACES WHEN SLOW", anr_traces_path);
-            i++;
-        }
-    }
+    AddAnrTraceFiles();
 
     int dumped = 0;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -1055,8 +1016,8 @@
             const char *name = tombstone_data[i].name;
             int fd = tombstone_data[i].fd;
             dumped = 1;
-            if (zip_writer) {
-                if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
+            if (ds.IsZipping()) {
+                if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
                     MYLOGE("Unable to add tombstone %s to zip file\n", name);
                 }
             } else {
@@ -1070,229 +1031,294 @@
         printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
     }
 
-    dump_file("NETWORK DEV INFO", "/proc/net/dev");
-    dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
-    dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
-    dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
-    dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+    DumpFile("NETWORK DEV INFO", "/proc/net/dev");
+    DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
+    DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
+    DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+    DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
 
-    do_kmsg();
+    DoKmsg();
 
     /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
 
-    run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
+    RunCommand("NETWORK INTERFACES", {"ip", "link"});
 
-    run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
-    run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
+    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
 
-    run_command("IP RULES", 10, "ip", "rule", "show", NULL);
-    run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
+    RunCommand("IP RULES", {"ip", "rule", "show"});
+    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
 
     dump_route_tables();
 
-    run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
-    run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
-    run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
-    run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+    RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
+    RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
+    RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
+    RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
+               CommandOptions::WithTimeout(20).Build());
 
-#ifdef FWDUMP_bcmdhd
-    run_command("ND OFFLOAD TABLE", 5,
-            SU_PATH, "root", WLUTIL, "nd_hostip", NULL);
+    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+               CommandOptions::WithTimeout(10).Build());
 
-    run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
+    RunCommand("SYSTEM PROPERTIES", {"getprop"});
 
-    run_command("ND OFFLOAD STATUS (1)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
+    RunCommand("VOLD DUMP", {"vdc", "dump"});
+    RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
 
-#endif
-    dump_file("INTERRUPTS (1)", "/proc/interrupts");
+    RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
 
-    run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
+    RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
 
-#ifdef FWDUMP_bcmdhd
-    run_command("DUMP WIFI STATUS", 20,
-            SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
-
-    run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
-
-    run_command("ND OFFLOAD STATUS (2)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
-#endif
-    dump_file("INTERRUPTS (2)", "/proc/interrupts");
-
-    print_properties();
-
-    run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
-    run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
-
-    run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
-
-    run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+    RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
 
     printf("------ BACKLIGHTS ------\n");
     printf("LCD brightness=");
-    dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
+    DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
     printf("Button brightness=");
-    dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
+    DumpFile("", "/sys/class/leds/button-backlight/brightness");
     printf("Keyboard brightness=");
-    dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
+    DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
     printf("ALS mode=");
-    dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
+    DumpFile("", "/sys/class/leds/lcd-backlight/als");
     printf("LCD driver registers:\n");
-    dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
+    DumpFile("", "/sys/class/leds/lcd-backlight/registers");
     printf("\n");
 
     /* Binder state is expensive to look at as it uses a lot of memory. */
-    dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
-    dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
-    dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
-    dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
-    dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
+    DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
+    DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
+    DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
+    DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
+    DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
 
-    printf("========================================================\n");
-    printf("== Board\n");
-    printf("========================================================\n");
+    ds.DumpstateBoard();
 
-    dumpstate_board();
-    printf("\n");
-
-    /* Migrate the ril_dumpstate to a dumpstate_board()? */
-    char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
-    property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
-    if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
-        if (is_user_build()) {
-            // su does not exist on user builds, so try running without it.
-            // This way any implementations of vril-dump that do not require
-            // root can run on user builds.
-            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
-                    "vril-dump", NULL);
-        } else {
-            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
-                    SU_PATH, "root", "vril-dump", NULL);
+    /* Migrate the ril_dumpstate to a device specific dumpstate? */
+    int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
+    if (rilDumpstateTimeout > 0) {
+        // su does not exist on user builds, so try running without it.
+        // This way any implementations of vril-dump that do not require
+        // root can run on user builds.
+        CommandOptions::CommandOptionsBuilder options =
+            CommandOptions::WithTimeout(rilDumpstateTimeout);
+        if (!PropertiesHelper::IsUserBuild()) {
+            options.AsRoot();
         }
+        RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
     }
 
     printf("========================================================\n");
     printf("== Android Framework Services\n");
     printf("========================================================\n");
 
-    run_command("DUMPSYS", 60, "dumpsys", "-t", "60", "--skip", "meminfo", "cpuinfo", NULL);
+    RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
+               10);
 
     printf("========================================================\n");
     printf("== Checkins\n");
     printf("========================================================\n");
 
-    run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "-t", "30", "batterystats", "-c", NULL);
-    run_command("CHECKIN MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "--checkin", NULL);
-    run_command("CHECKIN NETSTATS", 30, "dumpsys", "-t", "30", "netstats", "--checkin", NULL);
-    run_command("CHECKIN PROCSTATS", 30, "dumpsys", "-t", "30", "procstats", "-c", NULL);
-    run_command("CHECKIN USAGESTATS", 30, "dumpsys", "-t", "30", "usagestats", "-c", NULL);
-    run_command("CHECKIN PACKAGE", 30, "dumpsys", "-t", "30", "package", "--checkin", NULL);
+    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+    RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
+    RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
+    RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
+    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
+    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
 
     printf("========================================================\n");
     printf("== Running Application Activities\n");
     printf("========================================================\n");
 
-    run_command("APP ACTIVITIES", 30, "dumpsys", "-t", "30", "activity", "all", NULL);
+    RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Services\n");
     printf("========================================================\n");
 
-    run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "service", "all", NULL);
+    RunDumpsys("APP SERVICES", {"activity", "service", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Providers\n");
     printf("========================================================\n");
 
-    run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+    RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
 
-    // dump_modem_logs adds the modem logs if available to the bugreport.
+    printf("========================================================\n");
+    printf("== Dropbox crashes\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
+    RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
+
+    // DumpModemLogs adds the modem logs if available to the bugreport.
     // Do this at the end to allow for sufficient time for the modem logs to be
     // collected.
-    dump_modem_logs();
+    DumpModemLogs();
 
     printf("========================================================\n");
-    printf("== Final progress (pid %d): %d/%d (originally %d)\n",
-            getpid(), progress, weight_total, WEIGHT_TOTAL);
+    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
     printf("========================================================\n");
-    printf("== dumpstate: done\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
     printf("========================================================\n");
 }
 
-static void usage() {
-  fprintf(stderr,
-          "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-t]"
-          "[-z] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
-          "  -h: display this help message\n"
-          "  -b: play sound file instead of vibrate, at beginning of job\n"
-          "  -e: play sound file instead of vibrate, at end of job\n"
-          "  -o: write to file (instead of stdout)\n"
-          "  -d: append date to filename (requires -o)\n"
-          "  -p: capture screenshot to filename.png (requires -o)\n"
-          "  -t: only captures telephony sections\n"
-          "  -z: generate zipped file (requires -o)\n"
-          "  -s: write output to control socket (for init)\n"
-          "  -S: write file location to control socket (for init; requires -o and -z)"
-          "  -q: disable vibrate\n"
-          "  -B: send broadcast when finished (requires -o)\n"
-          "  -P: send broadcast when started and update system properties on "
-          "progress (requires -o and -B)\n"
-          "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
-          "shouldn't be used with -P)\n"
-          "  -V: sets the bugreport format version (valid values: %s)\n",
-          VERSION_DEFAULT.c_str());
+void Dumpstate::DumpstateBoard() {
+    DurationReporter duration_reporter("dumpstate_board()");
+    printf("========================================================\n");
+    printf("== Board\n");
+    printf("========================================================\n");
+
+    ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
+    if (dumpstate_device == nullptr) {
+        MYLOGE("No IDumpstateDevice implementation\n");
+        return;
+    }
+
+    if (!IsZipping()) {
+        MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
+        return;
+    }
+
+    std::string path = kDumpstateBoardPath;
+    MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
+
+    int fd =
+        TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+    if (fd < 0) {
+        MYLOGE("Could not open file %s: %s\n", path.c_str(), strerror(errno));
+        return;
+    }
+
+    native_handle_t* handle = native_handle_create(1, 0);
+    if (handle == nullptr) {
+        MYLOGE("Could not create native_handle\n");
+        return;
+    }
+    handle->data[0] = fd;
+
+    // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
+    android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle);
+    if (!status.isOk()) {
+        MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+        native_handle_close(handle);
+        native_handle_delete(handle);
+        return;
+    }
+
+    AddZipEntry("dumpstate-board.txt", path);
+    printf("*** See dumpstate-board.txt entry ***\n");
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+
+    if (remove(path.c_str()) != 0) {
+        MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
+    }
 }
 
-static void sigpipe_handler(int n) {
-    // don't complain to stderr or stdout
+static void ShowUsageAndExit(int exitCode = 1) {
+    fprintf(stderr,
+            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] "
+            "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+            "  -h: display this help message\n"
+            "  -b: play sound file instead of vibrate, at beginning of job\n"
+            "  -e: play sound file instead of vibrate, at end of job\n"
+            "  -o: write to file (instead of stdout)\n"
+            "  -d: append date to filename (requires -o)\n"
+            "  -p: capture screenshot to filename.png (requires -o)\n"
+            "  -z: generate zipped file (requires -o)\n"
+            "  -s: write output to control socket (for init)\n"
+            "  -S: write file location to control socket (for init; requires -o and -z)"
+            "  -q: disable vibrate\n"
+            "  -B: send broadcast when finished (requires -o)\n"
+            "  -P: send broadcast when started and update system properties on "
+            "progress (requires -o and -B)\n"
+            "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+            "shouldn't be used with -P)\n"
+            "  -v: prints the dumpstate header and exit\n");
+    exit(exitCode);
+}
+
+static void ExitOnInvalidArgs() {
+    fprintf(stderr, "invalid combination of args\n");
+    ShowUsageAndExit();
+}
+
+static void sig_handler(int) {
     _exit(EXIT_FAILURE);
 }
 
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
-   temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
-        time_t now) {
-    if (!add_zip_entry(bugreport_name, bugreport_path)) {
+static void register_sig_handler() {
+    struct sigaction sa;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = sig_handler;
+    sigaction(SIGPIPE, &sa, NULL); // broken pipe
+    sigaction(SIGSEGV, &sa, NULL); // segment fault
+    sigaction(SIGINT, &sa, NULL); // ctrl-c
+    sigaction(SIGTERM, &sa, NULL); // killed
+    sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
+bool Dumpstate::FinishZipFile() {
+    std::string entry_name = base_name_ + "-" + name_ + ".txt";
+    MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+           tmp_path_.c_str());
+    // Final timestamp
+    char date[80];
+    time_t the_real_now_please_stand_up = time(nullptr);
+    strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
+    MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
+           the_real_now_please_stand_up - ds.now_);
+
+    if (!ds.AddZipEntry(entry_name, tmp_path_)) {
         MYLOGE("Failed to add text entry to .zip file\n");
         return false;
     }
-    if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+    if (!AddTextZipEntry("main_entry.txt", entry_name)) {
         MYLOGE("Failed to add main_entry.txt to .zip file\n");
         return false;
     }
 
-    int32_t err = zip_writer->Finish();
-    if (err) {
-        MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+    // Add log file (which contains stderr output) to zip...
+    fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
+    if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
+        MYLOGE("Failed to add dumpstate log to .zip file\n");
+        return false;
+    }
+    // ... and re-opens it for further logging.
+    redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+    fprintf(stderr, "\n");
+
+    int32_t err = zip_writer_->Finish();
+    if (err != 0) {
+        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    if (is_user_build()) {
-        MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
-        if (remove(bugreport_path.c_str())) {
-            ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
-        }
-    } else {
-        MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+    // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+    ds.zip_file.reset(nullptr);
+
+    MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+    if (remove(tmp_path_.c_str()) != 0) {
+        MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
     }
 
     return true;
 }
 
 static std::string SHA256_file_hash(std::string filepath) {
-    ScopedFd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC
-            | O_NOFOLLOW)));
-    if (fd.get() == -1) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
+            | O_CLOEXEC | O_NOFOLLOW)));
+    if (fd == -1) {
         MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno));
         return NULL;
     }
 
     SHA256_CTX ctx;
-    SHA256_init(&ctx);
+    SHA256_Init(&ctx);
 
     std::vector<uint8_t> buffer(65536);
     while (1) {
@@ -1304,21 +1330,48 @@
             return NULL;
         }
 
-        SHA256_update(&ctx, buffer.data(), bytes_read);
+        SHA256_Update(&ctx, buffer.data(), bytes_read);
     }
 
-    uint8_t hash[SHA256_DIGEST_SIZE];
-    memcpy(hash, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
-    char hash_buffer[SHA256_DIGEST_SIZE * 2 + 1];
-    for(size_t i = 0; i < SHA256_DIGEST_SIZE; i++) {
+    uint8_t hash[SHA256_DIGEST_LENGTH];
+    SHA256_Final(hash, &ctx);
+
+    char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1];
+    for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
         sprintf(hash_buffer + (i * 2), "%02x", hash[i]);
     }
     hash_buffer[sizeof(hash_buffer) - 1] = 0;
     return std::string(hash_buffer);
 }
 
+static void SendBroadcast(const std::string& action, const std::vector<std::string>& args) {
+    // clang-format off
+    std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0",
+                    "--receiver-foreground", "--receiver-include-background", "-a", action};
+    // clang-format on
+
+    am.insert(am.end(), args.begin(), args.end());
+
+    RunCommand("", am,
+               CommandOptions::WithTimeout(20)
+                   .Log("Sending broadcast: '%s'\n")
+                   .Always()
+                   .DropRoot()
+                   .RedirectStderr()
+                   .Build());
+}
+
+static void Vibrate(int duration_ms) {
+    // clang-format off
+    RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"},
+               CommandOptions::WithTimeout(10)
+                   .Log("Vibrate: '%s'\n")
+                   .Always()
+                   .Build());
+    // clang-format on
+}
+
 int main(int argc, char *argv[]) {
-    struct sigaction sigact;
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -1327,33 +1380,15 @@
     int use_control_socket = 0;
     int do_fb = 0;
     int do_broadcast = 0;
-    int do_early_screenshot = 0;
     int is_remote_mode = 0;
+    bool show_header_only = false;
+    bool do_start_service = false;
     bool telephony_only = false;
 
-    std::string version = VERSION_DEFAULT;
-
-    now = time(NULL);
-
-    MYLOGI("begin\n");
-
-    /* gets the sequential id */
-    char last_id[PROPERTY_VALUE_MAX];
-    property_get("dumpstate.last_id", last_id, "0");
-    id = strtoul(last_id, NULL, 10) + 1;
-    snprintf(last_id, sizeof(last_id), "%lu", id);
-    property_set("dumpstate.last_id", last_id);
-    MYLOGI("dumpstate id: %lu\n", id);
-
-    /* clear SIGPIPE handler */
-    memset(&sigact, 0, sizeof(sigact));
-    sigact.sa_handler = sigpipe_handler;
-    sigaction(SIGPIPE, &sigact, NULL);
-
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
 
-    FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
+    FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
     if (oom_adj) {
         fputs("-1000", oom_adj);
         fclose(oom_adj);
@@ -1367,60 +1402,146 @@
     }
 
     /* parse arguments */
-    std::string args;
-    format_args(argc, const_cast<const char **>(argv), &args);
-    MYLOGD("Dumpstate command line: %s\n", args.c_str());
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzptPBRSV:")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
         switch (c) {
-            case 'd': do_add_date = 1;          break;
-            case 't': telephony_only = true;    break;
-            case 'z': do_zip_file = 1;          break;
-            case 'o': use_outfile = optarg;     break;
-            case 's': use_socket = 1;           break;
-            case 'S': use_control_socket = 1;   break;
-            case 'v': break;  // compatibility no-op
-            case 'q': do_vibrate = 0;           break;
-            case 'p': do_fb = 1;                break;
-            case 'P': do_update_progress = 1;   break;
-            case 'R': is_remote_mode = 1;       break;
-            case 'B': do_broadcast = 1;         break;
-            case 'V': version = optarg;         break;
-            case '?': printf("\n");
+            // clang-format off
+            case 'd': do_add_date = 1;            break;
+            case 'z': do_zip_file = 1;            break;
+            case 'o': use_outfile = optarg;       break;
+            case 's': use_socket = 1;             break;
+            case 'S': use_control_socket = 1;     break;
+            case 'v': show_header_only = true;    break;
+            case 'q': do_vibrate = 0;             break;
+            case 'p': do_fb = 1;                  break;
+            case 'P': ds.update_progress_ = true; break;
+            case 'R': is_remote_mode = 1;         break;
+            case 'B': do_broadcast = 1;           break;
+            case 'V':                             break; // compatibility no-op
             case 'h':
-                usage();
-                exit(1);
+                ShowUsageAndExit(0);
+                break;
+            default:
+                fprintf(stderr, "Invalid option: %c\n", c);
+                ShowUsageAndExit();
+                // clang-format on
         }
     }
 
-    if ((do_zip_file || do_add_date || do_update_progress || do_broadcast) && !use_outfile) {
-        usage();
-        exit(1);
+    // TODO: use helper function to convert argv into a string
+    for (int i = 0; i < argc; i++) {
+        ds.args_ += argv[i];
+        if (i < argc - 1) {
+            ds.args_ += " ";
+        }
+    }
+
+    ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+    if (!ds.extra_options_.empty()) {
+        // Framework uses a system property to override some command-line args.
+        // Currently, it contains the type of the requested bugreport.
+        if (ds.extra_options_ == "bugreportplus") {
+            // Currently, the dumpstate binder is only used by Shell to update progress.
+            do_start_service = true;
+            ds.update_progress_ = true;
+            do_fb = 0;
+        } else if (ds.extra_options_ == "bugreportremote") {
+            do_vibrate = 0;
+            is_remote_mode = 1;
+            do_fb = 0;
+        } else if (ds.extra_options_ == "bugreportwear") {
+            ds.update_progress_ = true;
+        } else if (ds.extra_options_ == "bugreporttelephony") {
+            telephony_only = true;
+        } else {
+            MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
+        }
+        // Reset the property
+        android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
+    }
+
+    ds.notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, "");
+    if (!ds.notification_title.empty()) {
+        // Reset the property
+        android::base::SetProperty(PROPERTY_EXTRA_TITLE, "");
+
+        ds.notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
+        if (!ds.notification_description.empty()) {
+            // Reset the property
+            android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
+        }
+        MYLOGD("notification (title:  %s, description: %s)\n",
+               ds.notification_title.c_str(), ds.notification_description.c_str());
+    }
+
+    if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
+        ExitOnInvalidArgs();
     }
 
     if (use_control_socket && !do_zip_file) {
-        usage();
+        ExitOnInvalidArgs();
+    }
+
+    if (ds.update_progress_ && !do_broadcast) {
+        ExitOnInvalidArgs();
+    }
+
+    if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+        ExitOnInvalidArgs();
+    }
+
+    if (ds.version_ == VERSION_DEFAULT) {
+        ds.version_ = VERSION_CURRENT;
+    }
+
+    if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+        MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+               ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+               VERSION_SPLIT_ANR.c_str());
         exit(1);
     }
 
-    if (do_update_progress && !do_broadcast) {
-        usage();
-        exit(1);
+    if (show_header_only) {
+        ds.PrintHeader();
+        exit(0);
     }
 
-    if (is_remote_mode && (do_update_progress || !do_broadcast || !do_zip_file || !do_add_date)) {
-        usage();
-        exit(1);
+    /* redirect output if needed */
+    bool is_redirecting = !use_socket && use_outfile;
+
+    // TODO: temporarily set progress until it's part of the Dumpstate constructor
+    std::string stats_path =
+        is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
+                       : "";
+    ds.progress_.reset(new Progress(stats_path));
+
+    /* gets the sequential id */
+    uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+    ds.id_ = ++last_id;
+    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+
+    MYLOGI("begin\n");
+
+    register_sig_handler();
+
+    if (do_start_service) {
+        MYLOGI("Starting 'dumpstate' service\n");
+        android::status_t ret;
+        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+            MYLOGE("Unable to start DumpstateService: %d\n", ret);
+        }
     }
 
-    if (version != VERSION_DEFAULT) {
-      usage();
-      exit(1);
+    if (PropertiesHelper::IsDryRun()) {
+        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
-    MYLOGI("bugreport format version: %s\n", version.c_str());
+    MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+           ds.extra_options_.c_str());
 
-    do_early_screenshot = do_update_progress;
+    MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
+
+    ds.do_early_screenshot_ = ds.update_progress_;
 
     // If we are going to use a socket, do it as early as possible
     // to avoid timeouts from bugreport.
@@ -1430,98 +1551,74 @@
 
     if (use_control_socket) {
         MYLOGD("Opening control socket\n");
-        control_socket_fd = open_socket("dumpstate");
-        do_update_progress = 1;
+        ds.control_socket_fd_ = open_socket("dumpstate");
+        ds.update_progress_ = 1;
     }
 
-    /* full path of the temporary file containing the bugreport */
-    std::string tmp_path;
-
-    /* full path of the file containing the dumpstate logs*/
-    std::string log_path;
-
-    /* full path of the systrace file, when enabled */
-    std::string systrace_path;
-
-    /* full path of the temporary file containing the screenshot (when requested) */
-    std::string screenshot_path;
-
-    /* base name (without suffix or extensions) of the bugreport files */
-    std::string base_name;
-
-    /* pointer to the actual path, be it zip or text */
-    std::string path;
-
-    /* pointer to the zipped file */
-    std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
-    /* redirect output if needed */
-    bool is_redirecting = !use_socket && use_outfile;
-
     if (is_redirecting) {
-        bugreport_dir = dirname(use_outfile);
-        base_name = basename(use_outfile);
+        ds.bugreport_dir_ = dirname(use_outfile);
+        std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
+        std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
+        ds.base_name_ = android::base::StringPrintf("%s-%s-%s", basename(use_outfile),
+                                                    device_name.c_str(), build_id.c_str());
         if (do_add_date) {
             char date[80];
-            strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&now));
-            suffix = date;
+            strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
+            ds.name_ = date;
         } else {
-            suffix = "undated";
+            ds.name_ = "undated";
         }
-        char build_id[PROPERTY_VALUE_MAX];
-        property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
-        base_name = base_name + "-" + build_id;
-        if (telephony_only) {
-            base_name = base_name + "-telephony";
-        }
-        if (do_fb) {
-            // TODO: if dumpstate was an object, the paths could be internal variables and then
-            // we could have a function to calculate the derived values, such as:
-            //     screenshot_path = GetPath(".png");
-            screenshot_path = bugreport_dir + "/" + base_name + "-" + suffix + ".png";
-        }
-        tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
-        log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
-                + std::to_string(getpid()) + ".txt";
 
-        MYLOGD("Bugreport dir: %s\n"
-                "Base name: %s\n"
-                "Suffix: %s\n"
-                "Log path: %s\n"
-                "Temporary path: %s\n"
-                "Screenshot path: %s\n",
-                bugreport_dir.c_str(), base_name.c_str(), suffix.c_str(),
-                log_path.c_str(), tmp_path.c_str(), screenshot_path.c_str());
+        if (telephony_only) {
+            ds.base_name_ += "-telephony";
+        }
+
+        if (do_fb) {
+            ds.screenshot_path_ = ds.GetPath(".png");
+        }
+        ds.tmp_path_ = ds.GetPath(".tmp");
+        ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
+
+        MYLOGD(
+            "Bugreport dir: %s\n"
+            "Base name: %s\n"
+            "Suffix: %s\n"
+            "Log path: %s\n"
+            "Temporary path: %s\n"
+            "Screenshot path: %s\n",
+            ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
+            ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
         if (do_zip_file) {
-            path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
-            MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
-            create_parent_dirs(path.c_str());
-            zip_file.reset(fopen(path.c_str(), "wb"));
-            if (!zip_file) {
-                MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
+            ds.path_ = ds.GetPath(".zip");
+            MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+            create_parent_dirs(ds.path_.c_str());
+            ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+            if (ds.zip_file == nullptr) {
+                MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
                 do_zip_file = 0;
             } else {
-                zip_writer.reset(new ZipWriter(zip_file.get()));
+                ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
             }
-            add_text_zip_entry("version.txt", version);
+            ds.AddTextZipEntry("version.txt", ds.version_);
         }
 
-        if (do_update_progress) {
+        if (ds.update_progress_) {
             if (do_broadcast) {
                 // clang-format off
+
                 std::vector<std::string> am_args = {
-                     "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                     "--es", "android.intent.extra.NAME", suffix,
-                     "--ei", "android.intent.extra.ID", std::to_string(id),
-                     "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                     "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+                     "--receiver-permission", "android.permission.DUMP",
+                     "--es", "android.intent.extra.NAME", ds.name_,
+                     "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+                     "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+                     "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
                 };
                 // clang-format on
-                send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+                SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
             }
             if (use_control_socket) {
-                dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
             }
         }
     }
@@ -1533,66 +1630,61 @@
         fclose(cmdline);
     }
 
-    /* open the vibrator before dropping root */
-    std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
     if (do_vibrate) {
-        vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
-        if (vibrator) {
-            vibrate(vibrator.get(), 150);
-        }
+        Vibrate(150);
     }
 
-    if (do_fb && do_early_screenshot) {
-        if (screenshot_path.empty()) {
+    if (do_fb && ds.do_early_screenshot_) {
+        if (ds.screenshot_path_.empty()) {
             // should not have happened
             MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
         } else {
             MYLOGI("taking early screenshot\n");
-            take_screenshot(screenshot_path);
-            MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
-            if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
-                MYLOGE("Unable to change ownership of screenshot file %s: %s\n",
-                        screenshot_path.c_str(), strerror(errno));
-            }
+            ds.TakeScreenshot();
         }
     }
 
     if (do_zip_file) {
-        if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
-            MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+        if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+            MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+                   strerror(errno));
         }
     }
 
     if (is_redirecting) {
-        redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
-        if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+        if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
-                    log_path.c_str(), strerror(errno));
+                   ds.log_path_.c_str(), strerror(errno));
         }
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
-        redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
-        if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+        if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
-                    tmp_path.c_str(), strerror(errno));
+                   ds.tmp_path_.c_str(), strerror(errno));
         }
     }
+
+    // Don't buffer stdout
+    setvbuf(stdout, nullptr, _IONBF, 0);
+
     // NOTE: there should be no stdout output until now, otherwise it would break the header.
     // In particular, DurationReport objects should be created passing 'title, NULL', so their
     // duration is logged into MYLOG instead.
-    print_header(version);
+    ds.PrintHeader();
 
     if (telephony_only) {
-        dump_iptables();
-        if (!drop_root_user()) {
+        DumpIpTables();
+        if (!DropRootUser()) {
             return -1;
         }
         do_dmesg();
-        do_logcat();
-        do_kmsg();
-        dumpstate_board();
-        dump_modem_logs();
+        DoLogcat();
+        DoKmsg();
+        ds.DumpstateBoard();
+        DumpModemLogs();
     } else {
         // Dumps systrace right away, otherwise it will be filled with unnecessary events.
         // First try to dump anrd trace if the daemon is running. Otherwise, dump
@@ -1601,40 +1693,44 @@
             dump_systrace();
         }
 
-        // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
-        dump_raft();
-
         // Invoking the following dumpsys calls before dump_traces() to try and
         // keep the system stats as close to its initial state as possible.
-        run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
-        run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
+        RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
+                   CommandOptions::WithTimeout(90).DropRoot().Build());
+        RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
+                   CommandOptions::WithTimeout(10).DropRoot().Build());
+
+        // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+        dump_raft();
 
         /* collect stack traces from Dalvik and native processes (needs root) */
         dump_traces_path = dump_traces();
 
         /* Run some operations that require root. */
         get_tombstone_fds(tombstone_data);
-        add_dir(RECOVERY_DIR, true);
-        add_dir(RECOVERY_DATA_DIR, true);
-        add_dir(LOGPERSIST_DATA_DIR, false);
-        if (!is_user_build()) {
-            add_dir(PROFILE_DATA_DIR_CUR, true);
-            add_dir(PROFILE_DATA_DIR_REF, true);
+        ds.AddDir(RECOVERY_DIR, true);
+        ds.AddDir(RECOVERY_DATA_DIR, true);
+        ds.AddDir(LOGPERSIST_DATA_DIR, false);
+        if (!PropertiesHelper::IsUserBuild()) {
+            ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+            ds.AddDir(PROFILE_DATA_DIR_REF, true);
         }
         add_mountinfo();
-        dump_iptables();
+        DumpIpTables();
 
         // Capture any IPSec policies in play.  No keys are exposed here.
-        run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
+        RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
+                   CommandOptions::WithTimeout(10).Build());
 
         // Run ss as root so we can see socket marks.
-        run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
+        RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
+                   CommandOptions::WithTimeout(10).Build());
 
-        if (!drop_root_user()) {
+        if (!DropRootUser()) {
             return -1;
         }
 
-        dumpstate(do_early_screenshot ? "": screenshot_path, version);
+        dumpstate();
     }
 
     /* close output if needed */
@@ -1646,125 +1742,136 @@
     if (use_outfile) {
 
         /* check if user changed the suffix using system properties */
-        char key[PROPERTY_KEY_MAX];
-        char value[PROPERTY_VALUE_MAX];
-        snprintf(key, sizeof(key), "dumpstate.%d.name", getpid());
-        property_get(key, value, "");
+        std::string name = android::base::GetProperty(
+            android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
         bool change_suffix= false;
-        if (value[0]) {
+        if (!name.empty()) {
             /* must whitelist which characters are allowed, otherwise it could cross directories */
             std::regex valid_regex("^[-_a-zA-Z0-9]+$");
-            if (std::regex_match(value, valid_regex)) {
+            if (std::regex_match(name.c_str(), valid_regex)) {
                 change_suffix = true;
             } else {
-                MYLOGE("invalid suffix provided by user: %s\n", value);
+                MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
             }
         }
         if (change_suffix) {
-            MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), value);
-            suffix = value;
-            if (!screenshot_path.empty()) {
-                std::string new_screenshot_path =
-                        bugreport_dir + "/" + base_name + "-" + suffix + ".png";
-                if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) {
-                    MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(),
-                            new_screenshot_path.c_str(), strerror(errno));
+            MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+            ds.name_ = name;
+            if (!ds.screenshot_path_.empty()) {
+                std::string new_screenshot_path = ds.GetPath(".png");
+                if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+                    MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+                           new_screenshot_path.c_str(), strerror(errno));
                 } else {
-                    screenshot_path = new_screenshot_path;
+                    ds.screenshot_path_ = new_screenshot_path;
                 }
             }
         }
 
         bool do_text_file = true;
         if (do_zip_file) {
-            std::string entry_name = base_name + "-" + suffix + ".txt";
-            MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
-            if (!finish_zip_file(entry_name, tmp_path, now)) {
+            if (!ds.FinishZipFile()) {
                 MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
                 do_text_file = true;
             } else {
                 do_text_file = false;
                 // Since zip file is already created, it needs to be renamed.
-                std::string new_path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
-                if (path != new_path) {
-                    MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
-                    if (rename(path.c_str(), new_path.c_str())) {
-                        MYLOGE("rename(%s, %s): %s\n", path.c_str(),
-                                new_path.c_str(), strerror(errno));
+                std::string new_path = ds.GetPath(".zip");
+                if (ds.path_ != new_path) {
+                    MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+                    if (rename(ds.path_.c_str(), new_path.c_str())) {
+                        MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+                               strerror(errno));
                     } else {
-                        path = new_path;
+                        ds.path_ = new_path;
                     }
                 }
             }
         }
         if (do_text_file) {
-            path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
-            MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
-            if (rename(tmp_path.c_str(), path.c_str())) {
-                MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
-                path.clear();
+            ds.path_ = ds.GetPath(".txt");
+            MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+                   ds.tmp_path_.c_str());
+            if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+                MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+                       strerror(errno));
+                ds.path_.clear();
             }
         }
         if (use_control_socket) {
             if (do_text_file) {
-                dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
-                        "for more details\n", log_path.c_str());
+                dprintf(ds.control_socket_fd_,
+                        "FAIL:could not create zip file, check %s "
+                        "for more details\n",
+                        ds.log_path_.c_str());
             } else {
-                dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
             }
         }
     }
 
     /* vibrate a few but shortly times to let user know it's finished */
-    if (vibrator) {
-        for (int i = 0; i < 3; i++) {
-            vibrate(vibrator.get(), 75);
-            usleep((75 + 50) * 1000);
-        }
+    for (int i = 0; i < 3; i++) {
+        Vibrate(75);
+        usleep((75 + 50) * 1000);
     }
 
     /* tell activity manager we're done */
     if (do_broadcast) {
-        if (!path.empty()) {
-            MYLOGI("Final bugreport path: %s\n", path.c_str());
+        if (!ds.path_.empty()) {
+            MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
             // clang-format off
+
             std::vector<std::string> am_args = {
-                 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                 "--ei", "android.intent.extra.ID", std::to_string(id),
-                 "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                 "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
-                 "--es", "android.intent.extra.BUGREPORT", path,
-                 "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+                 "--receiver-permission", "android.permission.DUMP",
+                 "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+                 "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+                 "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
+                 "--es", "android.intent.extra.BUGREPORT", ds.path_,
+                 "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
             };
             // clang-format on
             if (do_fb) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.SCREENSHOT");
-                am_args.push_back(screenshot_path);
+                am_args.push_back(ds.screenshot_path_);
+            }
+            if (!ds.notification_title.empty()) {
+                am_args.push_back("--es");
+                am_args.push_back("android.intent.extra.TITLE");
+                am_args.push_back(ds.notification_title);
+                if (!ds.notification_description.empty()) {
+                    am_args.push_back("--es");
+                    am_args.push_back("android.intent.extra.DESCRIPTION");
+                    am_args.push_back(ds.notification_description);
+                }
             }
             if (is_remote_mode) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
-                am_args.push_back(SHA256_file_hash(path));
-                send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
+                am_args.push_back(SHA256_file_hash(ds.path_));
+                SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED",
+                              am_args);
             } else {
-                send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
+                SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
             }
         } else {
             MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
         }
     }
 
-    MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
-    MYLOGI("done\n");
+    MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
+           ds.progress_->GetInitialMax());
+    ds.progress_->Save();
+    MYLOGI("done (id %d)\n", ds.id_);
 
     if (is_redirecting) {
         fclose(stderr);
     }
 
-    if (use_control_socket && control_socket_fd != -1) {
-      MYLOGD("Closing control socket\n");
-      close(control_socket_fd);
+    if (use_control_socket && ds.control_socket_fd_ != -1) {
+        MYLOGD("Closing control socket\n");
+        close(ds.control_socket_fd_);
     }
 
     return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 514af59..f02303b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -14,93 +14,341 @@
  * limitations under the License.
  */
 
-#ifndef _DUMPSTATE_H_
-#define _DUMPSTATE_H_
-
-/* When defined, skips the real dumps and just print the section headers.
-   Useful when debugging dumpstate itself. */
-//#define _DUMPSTATE_DRY_RUN_
-
-#ifdef _DUMPSTATE_DRY_RUN_
-#define ON_DRY_RUN_RETURN(X) return X
-#define ON_DRY_RUN(code) code
-#else
-#define ON_DRY_RUN_RETURN(X)
-#define ON_DRY_RUN(code)
-#endif
-
-#ifndef MYLOGD
-#define MYLOGD(...) fprintf(stderr, __VA_ARGS__); ALOGD(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGI
-#define MYLOGI(...) fprintf(stderr, __VA_ARGS__); ALOGI(__VA_ARGS__);
-#endif
-
-#ifndef MYLOGE
-#define MYLOGE(...) fprintf(stderr, __VA_ARGS__); ALOGE(__VA_ARGS__);
-#endif
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_
 
 #include <time.h>
 #include <unistd.h>
 #include <stdbool.h>
 #include <stdio.h>
+
+#include <string>
 #include <vector>
 
-#define SU_PATH "/system/xbin/su"
+#include <android-base/macros.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>
+// TODO: remove once not used
+#define MAX_ARGS_ARRAY_SIZE 1000
+
+// TODO: move everything under this namespace
+// TODO: and then remove explicitly android::os::dumpstate:: prefixes
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpstateTest;
+class ProgressTest;
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
+
+// TODO: remove once moved to HAL
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef void (for_each_pid_func)(int, const char *);
-typedef void (for_each_tid_func)(int, int, const char *);
-
-/* Estimated total weight of bugreport generation.
+/*
+ * Helper class used to report how long it takes for a section to finish.
  *
- * Each section contributes to the total weight by an individual weight, so the overall progress
- * can be calculated by dividing the all completed weight by the total weight.
+ * Typical usage:
  *
- * This value is defined empirically and it need to be adjusted as more sections are added.
+ *    DurationReporter duration_reporter(title);
  *
- * It does not need to match the exact sum of all sections, but ideally it should to be slight more
- * than such sum: a value too high will cause the bugreport to finish before the user expected (for
- * example, jumping from 70% to 100%), while a value too low will cause the progress to get stuck
- * at an almost-finished value (like 99%) for a while.
  */
-static const int WEIGHT_TOTAL = 6500;
+class DurationReporter {
+  public:
+    DurationReporter(const std::string& title, bool log_only = false);
 
-/* Most simple commands have 10 as timeout, so 5 is a good estimate */
-static const int WEIGHT_FILE = 5;
+    ~DurationReporter();
+
+  private:
+    std::string title_;
+    bool log_only_;
+    uint64_t started_;
+
+    DISALLOW_COPY_AND_ASSIGN(DurationReporter);
+};
 
 /*
- * TODO: the dumpstate internal state is getting fragile; for example, this variable is defined
- * here, declared at utils.cpp, and used on utils.cpp and dumpstate.cpp.
- * It would be better to take advantage of the C++ migration and encapsulate the state in an object,
- * but that will be better handled in a major C++ refactoring, which would also get rid of other C
- * idioms (like using std::string instead of char*, removing varargs, etc...) */
-extern int do_update_progress, progress, weight_total, control_socket_fd;
+ * Keeps track of current progress and estimated max, saving stats on file to tune up future runs.
+ *
+ * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall
+ * progress can be calculated by dividing the estimate max progress by the current progress.
+ *
+ * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but
+ * it's adjusted after each dumpstate run by storing the average duration in a file.
+ *
+ */
+class Progress {
+    friend class android::os::dumpstate::ProgressTest;
+    friend class android::os::dumpstate::DumpstateTest;
 
-/* full path of the directory where the bugreport files will be written */
-extern std::string bugreport_dir;
+  public:
+    /*
+     * Default estimation of the max duration of a bugreport generation.
+     *
+     * It does not need to match the exact sum of all sections, but ideally it should to be slight
+     * more than such sum: a value too high will cause the bugreport to finish before the user
+     * expected (for example, jumping from 70% to 100%), while a value too low will cause the
+     * progress to get stuck at an almost-finished value (like 99%) for a while.
+     *
+     * This constant is only used when the average duration from previous runs cannot be used.
+     */
+    static const int kDefaultMax;
 
-/* root dir for all files copied as-is into the bugreport. */
-extern const std::string ZIP_ROOT_DIR;
+    Progress(const std::string& path = "");
 
-/* Checkes whether dumpstate is generating a zipped bugreport. */
-bool is_zipping();
+    // Gets the current progress.
+    int32_t Get() const;
 
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
+    // Gets the current estimated max progress.
+    int32_t GetMax() const;
 
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+    // Gets the initial estimated max progress.
+    int32_t GetInitialMax() const;
 
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const char *dir, bool recursive);
+    // Increments progress (ignored if not positive).
+    // Returns `true` if the max progress increased as well.
+    bool Inc(int32_t delta);
 
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path);
+    // Persist the stats.
+    void Save();
+
+    void Dump(int fd, const std::string& prefix) const;
+
+  private:
+    Progress(int32_t initial_max, float growth_factor,
+             const std::string& path = "");                                // Used by test cases.
+    Progress(int32_t initial_max, int32_t progress, float growth_factor);  // Used by test cases.
+    void Load();
+    int32_t initial_max_;
+    int32_t progress_;
+    int32_t max_;
+    float growth_factor_;
+    int32_t n_runs_;
+    int32_t average_max_;
+    const std::string& path_;
+};
+
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.md for more info.
+ */
+static std::string VERSION_CURRENT = "1.0";
+
+/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
+
+/*
+ * Main class driving a bugreport generation.
+ *
+ * Currently, it only contains variables that are accessed externally, but gradually the functions
+ * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
+ */
+class Dumpstate {
+    friend class DumpstateTest;
+
+  public:
+    static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
+
+    static Dumpstate& GetInstance();
+
+    /* Checkes whether dumpstate is generating a zipped bugreport. */
+    bool IsZipping() const;
+
+    /*
+     * Forks a command, waits for it to finish, and returns its status.
+     *
+     * |title| description of the command printed on `stdout` (or empty to skip
+     * description).
+     * |full_command| array containing the command (first entry) and its arguments.
+     * Must contain at least one element.
+     * |options| optional argument defining the command's behavior.
+     */
+    int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+                   const android::os::dumpstate::CommandOptions& options =
+                       android::os::dumpstate::CommandOptions::DEFAULT);
+
+    /*
+     * Runs `dumpsys` with the given arguments, automatically setting its timeout
+     * (`-t` argument)
+     * according to the command options.
+     *
+     * |title| description of the command printed on `stdout` (or empty to skip
+     * description).
+     * |dumpsys_args| `dumpsys` arguments (except `-t`).
+     * |options| optional argument defining the command's behavior.
+     * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
+     * timeout from `options`)
+     */
+    void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+                    const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
+                    long dumpsys_timeout = 0);
+
+    /*
+     * Prints the contents of a file.
+     *
+     * |title| description of the command printed on `stdout` (or empty to skip
+     * description).
+     * |path| location of the file to be dumped.
+     */
+    int DumpFile(const std::string& title, const std::string& path);
+
+    /*
+     * Adds a new entry to the existing zip file.
+     * */
+    bool AddZipEntry(const std::string& entry_name, const std::string& entry_path);
+
+    /*
+     * Adds a new entry to the existing zip file.
+     */
+    bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+
+    /*
+     * Adds a text entry entry to the existing zip file.
+     */
+    bool AddTextZipEntry(const std::string& entry_name, const std::string& content);
+
+    /*
+     * Adds all files from a directory to the zipped bugreport file.
+     */
+    void AddDir(const std::string& dir, bool recursive);
+
+    /*
+     * Takes a screenshot and save it to the given `path`.
+     *
+     * If `path` is empty, uses a standard path based on the bugreport name.
+     */
+    void TakeScreenshot(const std::string& path = "");
+
+    /////////////////////////////////////////////////////////////////////
+    // TODO: members below should be private once refactor is finished //
+    /////////////////////////////////////////////////////////////////////
+
+    // TODO: temporary method until Dumpstate object is properly set
+    void SetProgress(std::unique_ptr<Progress> progress);
+
+    void DumpstateBoard();
+
+    /*
+     * Updates the overall progress of the bugreport generation by the given weight increment.
+     */
+    void UpdateProgress(int32_t delta);
+
+    /* Prints the dumpstate header on `stdout`. */
+    void PrintHeader() const;
+
+    /*
+     * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the
+     * temporary file.
+     */
+    bool FinishZipFile();
+
+    /* Gets the path of a bugreport file with the given suffix. */
+    std::string GetPath(const std::string& suffix) const;
+
+    // TODO: initialize fields on constructor
+
+    // dumpstate id - unique after each device reboot.
+    uint32_t id_;
+
+    // dumpstate pid
+    pid_t pid_;
+
+    // Whether progress updates should be published.
+    bool update_progress_ = false;
+
+    // How frequently the progess should be updated;the listener will only be notificated when the
+    // delta from the previous update is more than the threshold.
+    int32_t update_progress_threshold_ = 100;
+
+    // Last progress that triggered a listener updated
+    int32_t last_updated_progress_;
+
+    // Whether it should take an screenshot earlier in the process.
+    bool do_early_screenshot_ = false;
+
+    std::unique_ptr<Progress> progress_;
+
+    // When set, defines a socket file-descriptor use to report progress to bugreportz.
+    int control_socket_fd_ = -1;
+
+    // Bugreport format version;
+    std::string version_ = VERSION_CURRENT;
+
+    // Command-line arguments as string
+    std::string args_;
+
+    // Extra options passed as system property.
+    std::string extra_options_;
+
+    // Full path of the directory where the bugreport files will be written.
+    std::string bugreport_dir_;
+
+    // Full path of the temporary file containing the screenshot (when requested).
+    std::string screenshot_path_;
+
+    time_t now_;
+
+    // Base name (without suffix or extensions) of the bugreport files, typically
+    // `bugreport-BUILD_ID`.
+    std::string base_name_;
+
+    // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
+    // `-d`), but it could be changed by the user..
+    std::string name_;
+
+    // Full path of the temporary file containing the bugreport.
+    std::string tmp_path_;
+
+    // Full path of the file containing the dumpstate logs.
+    std::string log_path_;
+
+    // Pointer to the actual path, be it zip or text.
+    std::string path_;
+
+    // Pointer to the zipped file.
+    std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
+
+    // Pointer to the zip structure.
+    std::unique_ptr<ZipWriter> zip_writer_;
+
+    // Binder object listing to progress.
+    android::sp<android::os::IDumpstateListener> listener_;
+    std::string listener_name_;
+
+    // Notification title and description
+    std::string notification_title;
+    std::string notification_description;
+
+  private:
+    // Used by GetInstance() only.
+    Dumpstate(const std::string& version = VERSION_CURRENT);
+
+    DISALLOW_COPY_AND_ASSIGN(Dumpstate);
+};
+
+// for_each_pid_func = void (*)(int, const char*);
+// for_each_tid_func = void (*)(int, int, const char*);
+
+typedef void(for_each_pid_func)(int, const char*);
+typedef void(for_each_tid_func)(int, int, const char*);
 
 /* saves the the contents of a file as a long */
 int read_file_as_long(const char *path, long int *output);
@@ -116,37 +364,8 @@
  * to false when set to NULL. dump_from_fd will always be
  * called with title NULL.
  */
-int dump_files(const char *title, const char *dir,
-        bool (*skip)(const char *path),
-        int (*dump_from_fd)(const char *title, const char *path, int fd));
-
-// TODO: need to refactor all those run_command variations; there shold be just one, receiving an
-// optional CommandOptions objects with values such as run_always, drop_root, etc...
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
-int run_command(const char *title, int timeout_seconds, const char *command, ...);
-
-enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
-enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
-
-/* forks a command and waits for it to finish
-   first element of args is the command, and last must be NULL.
-   command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]);
-
-/* switch to non-root user and group */
-bool drop_root_user();
-
-/* sends a broadcast using Activity Manager */
-void send_broadcast(const std::string& action, const std::vector<std::string>& args);
-
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
-
-/* prints all the system properties */
-void print_properties();
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+               int (*dump_from_fd)(const char* title, const char* path, int fd));
 
 /** opens a socket and returns its file descriptor */
 int open_socket(const char *service);
@@ -154,9 +373,12 @@
 /* redirect output to a service control socket */
 void redirect_to_socket(FILE *redirect, const char *service);
 
-/* redirect output to a file */
+/* redirect output to a new file */
 void redirect_to_file(FILE *redirect, char *path);
 
+/* redirect output to an existing file */
+void redirect_to_existing_file(FILE *redirect, char *path);
+
 /* create leading directories, if necessary */
 void create_parent_dirs(const char *path);
 
@@ -187,15 +409,6 @@
 /* Play a sound via Stagefright */
 void play_sound(const char *path);
 
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-/* Takes a screenshot and save it to the given file */
-void take_screenshot(const std::string& path);
-
-/* Vibrates for a given durating (in milliseconds). */
-void vibrate(FILE* vibrator, int ms);
-
 /* Checks if a given path is a directory. */
 bool is_dir(const char* pathname);
 
@@ -208,34 +421,8 @@
 /** Gets command-line arguments. */
 void format_args(int argc, const char *argv[], std::string *args);
 
-/** Tells if the device is running a user build. */
-bool is_user_build();
-
-/*
- * Helper class used to report how long it takes for a section to finish.
- *
- * Typical usage:
- *
- *    DurationReporter duration_reporter(title);
- *
- */
-class DurationReporter {
-public:
-    DurationReporter(const char *title);
-    DurationReporter(const char *title, FILE* out);
-
-    ~DurationReporter();
-
-    static uint64_t nanotime();
-
-private:
-    const char* title_;
-    FILE* out_;
-    uint64_t started_;
-};
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* _DUMPSTATE_H_ */
+#endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 336db9f..2e72574 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,40 +17,3 @@
     class main
     disabled
     oneshot
-
-# bugreportplus is an enhanced version of bugreport that provides a better
-# user interface (like displaying progress and allowing user to enter details).
-# It's typically triggered by the power button or developer settings.
-service bugreportplus /system/bin/dumpstate -d -B -P -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportremote is an altered version of bugreport that is supposed to be
-# called not by human user of the device, but by DevicePolicyManagerService only when the
-# Device Owner explicitly requests it, and shared with the Device Policy Controller (DPC) app only
-# if the user consents
-# it will disable vibrations, screenshot taking and will not track progress or
-# allow user to enter any details
-service bugreportremote /system/bin/dumpstate -d -q -B -R -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/remote/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportwear is a wearable version of bugreport that displays progress and takes early
-# screenshot.
-service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportelefony is a lightweight version of bugreport that only includes a few, urgent
-# sections used to report telephony bugs.
-service bugreportelefony /system/bin/dumpstate -t -d -B -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
deleted file mode 100644
index fd840df..0000000
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dumpstate.h"
-
-void dumpstate_board(void)
-{
-}
-
diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/testdata/empty-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/dumpstate/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
new file mode 100644
index 0000000..7b7a187
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/testdata/multiple-lines.txt
new file mode 100644
index 0000000..bead103
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/testdata/single-line-with-newline.txt
new file mode 100644
index 0000000..cb48c82
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line-with-newline.txt
@@ -0,0 +1 @@
+I AM LINE1
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/testdata/single-line.txt
new file mode 100644
index 0000000..2f64046
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line.txt
@@ -0,0 +1 @@
+I AM LINE1
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
new file mode 100644
index 0000000..dad9fe8
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
@@ -0,0 +1 @@
+SIX_SIX_SIX 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
new file mode 100644
index 0000000..4facef9
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
@@ -0,0 +1 @@
+-666 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
new file mode 100644
index 0000000..42508f1
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
@@ -0,0 +1 @@
+4815162342 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
new file mode 100644
index 0000000..a23ba2c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
@@ -0,0 +1 @@
+666 FORTY_TWO
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
new file mode 100644
index 0000000..dd529b4
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
@@ -0,0 +1 @@
+666 -42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
new file mode 100644
index 0000000..b148b46
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
@@ -0,0 +1 @@
+666 4815162342
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
new file mode 100644
index 0000000..4a9466d
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
@@ -0,0 +1 @@
+N_RUNS AVERAGE
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
new file mode 100644
index 0000000..0aef60c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
@@ -0,0 +1 @@
+1 10
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/testdata/stats-two-runs.txt
new file mode 100644
index 0000000..9af1233
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-two-runs.txt
@@ -0,0 +1 @@
+2 15
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..1c19268
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
+#include "android/os/BnDumpstate.h"
+#include "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::IsNull;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::StrEq;
+using ::testing::StartsWith;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class DumpstateListenerMock : public IDumpstateListener {
+  public:
+    MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
+    MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+
+  protected:
+    MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+static int calls_;
+
+// Base class for all tests in this file
+class DumpstateBaseTest : public Test {
+  public:
+    virtual void SetUp() override {
+        calls_++;
+        SetDryRun(false);
+    }
+
+    void SetDryRun(bool dry_run) const {
+        PropertiesHelper::dry_run_ = dry_run;
+    }
+
+    void SetBuildType(const std::string& build_type) const {
+        PropertiesHelper::build_type_ = build_type;
+    }
+
+    bool IsStandalone() const {
+        return calls_ == 1;
+    }
+
+    void DropRoot() const {
+        DropRootUser();
+        uid_t uid = getuid();
+        ASSERT_EQ(2000, (int)uid);
+    }
+
+  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 kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
+    const std::string kEchoCommand = "/system/bin/echo";
+
+    /*
+     * Copies a text file fixture to a temporary file, returning it's path.
+     *
+     * Useful in cases where the test case changes the content of the tile.
+     */
+    std::string CopyTextFileFixture(const std::string& relative_name) {
+        std::string from = kTestDataPath + relative_name;
+        // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it
+        // around for poking when the test fails.
+        std::string to = kTestDataPath + relative_name + ".tmp";
+        ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str());
+        android::base::RemoveFileIfExists(to);
+        CopyTextFile(from, to);
+        return to.c_str();
+    }
+
+    // Need functions that returns void to use assertions -
+    // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement
+    void ReadFileToString(const std::string& path, std::string* content) {
+        ASSERT_TRUE(android::base::ReadFileToString(path, content))
+            << "could not read contents from " << path;
+    }
+    void WriteStringToFile(const std::string& content, const std::string& path) {
+        ASSERT_TRUE(android::base::WriteStringToFile(content, path))
+            << "could not write contents to " << path;
+    }
+
+  private:
+    void CopyTextFile(const std::string& from, const std::string& to) {
+        std::string content;
+        ReadFileToString(from, &content);
+        WriteStringToFile(content, to);
+    }
+};
+
+class DumpstateTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+        SetDryRun(false);
+        SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+        ds.progress_.reset(new Progress());
+        ds.update_progress_ = false;
+        ds.update_progress_threshold_ = 0;
+    }
+
+    // Runs a command and capture `stdout` and `stderr`.
+    int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options = CommandOptions::DEFAULT) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.RunCommand(title, full_command, options);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Dumps a file and capture `stdout` and `stderr`.
+    int DumpFile(const std::string& title, const std::string& path) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.DumpFile(title, path);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    void SetProgress(long progress, long initial_max, long threshold = 0) {
+        ds.update_progress_ = true;
+        ds.update_progress_threshold_ = threshold;
+        ds.last_updated_progress_ = 0;
+        ds.progress_.reset(new Progress(initial_max, progress, 1.2));
+    }
+
+    std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
+                                   int old_max = 0, bool update_progress = true) {
+        EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
+        EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
+
+        bool max_increased = old_max > 0;
+
+        std::string message = "";
+        if (max_increased) {
+            message =
+                android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max);
+        }
+
+        if (update_progress) {
+            message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
+                                                   listener_name.c_str(), progress, max);
+        }
+
+        return message;
+    }
+
+    // `stdout` and `stderr` from the last command ran.
+    std::string out, err;
+
+    Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+    EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out,
+                StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+    EXPECT_EQ(
+        0, RunCommand("", {kSimpleCommand},
+                      CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandRedirectStderr) {
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+                            CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
+                                ") ------\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandNotFound) {
+    EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+    EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+    EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateTest, RunCommandFails) {
+    EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+    EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+    // We don't know the exit code, so check just the prefix.
+    EXPECT_THAT(
+        out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+    EXPECT_THAT(
+        err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+    EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+                             CommandOptions::WithTimeout(1).Build()));
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+    CaptureStdout();
+    CaptureStderr();
+
+    std::thread t([=]() {
+        EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"},
+                                         CommandOptions::WithTimeout(100).Always().Build()));
+    });
+
+    // Capture pid and pre-sleep output.
+    sleep(1);  // Wait a little bit to make sure pid and 1st line were printed.
+    std::string err = GetCapturedStderr();
+    EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+    std::string out = GetCapturedStdout();
+    std::vector<std::string> lines = android::base::Split(out, "\n");
+    ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+    int pid = atoi(lines[0].c_str());
+    EXPECT_THAT(lines[1], StrEq("stdout line1"));
+    EXPECT_THAT(lines[2], IsEmpty());  // \n
+
+    // Then kill the process.
+    CaptureStdout();
+    CaptureStderr();
+    ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+    t.join();
+
+    // Finally, check output after murder.
+    out = GetCapturedStdout();
+    err = GetCapturedStderr();
+
+    EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+    EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 30);
+
+    EXPECT_CALL(*listener, onProgressUpdated(20));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
+    std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    EXPECT_CALL(*listener, onProgressUpdated(30));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Run a command that will increase maximum timeout.
+    EXPECT_CALL(*listener, onProgressUpdated(31));
+    EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30);  // 20% increase
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Make sure command ran while in dry_run is counted.
+    SetDryRun(true);
+    EXPECT_CALL(*listener, onProgressUpdated(35));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq(progress_message));
+
+    ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 8, 5);  // 8 max, 5 threshold
+
+    // First update should always be sent.
+    EXPECT_CALL(*listener, onProgressUpdated(1));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5).
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+
+    // Third update should be sent because it reaches threshold (6 - 1 = 5).
+    EXPECT_CALL(*listener, onProgressUpdated(6));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5).
+    // But max update should be sent.
+    EXPECT_CALL(*listener, onMaxProgressUpdated(10));  // 9 * 120% = 10.8 = 10
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandDropRoot() on test suite\n")
+        return;
+    }
+    // First check root case - only available when running with 'adb root'.
+    uid_t uid = getuid();
+    if (uid == 0) {
+        EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+        EXPECT_THAT(out, StrEq("0\nstdout\n"));
+        EXPECT_THAT(err, StrEq("stderr\n"));
+        return;
+    }
+    // Then run dropping root.
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).DropRoot().Build()));
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootUserBuild() on test suite\n")
+        return;
+    }
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    // We don't know the exact path of su, so we just check for the 'root ...' commands
+    EXPECT_THAT(out, StartsWith("Skipping"));
+    EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootNonUserBuild() on test suite\n")
+        return;
+    }
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
+    EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out,
+                StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) {
+    EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(err, IsEmpty());
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
+                                "such file or directory\n"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLines) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(
+        out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+    EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 30);
+
+    EXPECT_CALL(*listener, onProgressUpdated(5));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+
+    std::string progress_message =
+        GetProgressMessage(ds.listener_name_, 5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
+    EXPECT_THAT(err, StrEq(progress_message));
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+
+    ds.listener_.clear();
+}
+
+class DumpstateServiceTest : public DumpstateBaseTest {
+  public:
+    DumpstateService dss;
+};
+
+TEST_F(DumpstateServiceTest, SetListenerNoName) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerTwice) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+    ASSERT_THAT(token, NotNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+
+    token.clear();
+    EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+}
+
+class ProgressTest : public DumpstateBaseTest {
+  public:
+    Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
+        return Progress(max, growth_factor, path);
+    }
+
+    void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) {
+        std::string expected_content =
+            android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
+        std::string actual_content;
+        ReadFileToString(path, &actual_content);
+        ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
+    }
+};
+
+TEST_F(ProgressTest, SimpleTest) {
+    Progress progress;
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+
+    bool max_increased = progress.Inc(1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Ignore negative increase.
+    max_increased = progress.Inc(-1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsInsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value < max*20%
+    max_increased = progress.Inc(1);
+    EXPECT_EQ(11, progress.Get());
+    EXPECT_EQ(13, progress.GetMax());  // 11 average * 20% growth = 13.2 = 13
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsOutsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value > max*20%
+    max_increased = progress.Inc(5);
+    EXPECT_EQ(15, progress.Get());
+    EXPECT_EQ(18, progress.GetMax());  // 15 average * 20% growth = 18
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, InvalidPath) {
+    Progress progress("/devil/null");
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, EmptyFile) {
+    Progress progress(CopyTextFileFixture("empty-file.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLineBothNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+// Tests stats are properly saved when the file does not exists.
+TEST_F(ProgressTest, FirstTime) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it's failing when running as suite
+        MYLOGE("Skipping ProgressTest.FirstTime() on test suite\n")
+        return;
+    }
+
+    std::string path = kTestDataPath + "FirstTime.txt";
+    android::base::RemoveFileIfExists(path);
+
+    Progress run1(path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    run1.Save();
+    AssertStats(path, 1, 20);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 1 run.
+// Data on file is 1 run and 109 average.
+TEST_F(ProgressTest, SecondTime) {
+    std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt");
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(10, run1.GetInitialMax());
+    EXPECT_EQ(10, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 2 runs and (10 + 20)/ 2 = 15
+    run1.Save();
+    AssertStats(path, 2, 15);
+
+    Progress run2 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run2.Get());
+    EXPECT_EQ(15, run2.GetInitialMax());
+    EXPECT_EQ(15, run2.GetMax());
+
+    max_increased = run2.Inc(25);
+    EXPECT_EQ(25, run2.Get());
+    EXPECT_EQ(30, run2.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18
+    run2.Save();
+    AssertStats(path, 3, 18);
+
+    Progress run3 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run3.Get());
+    EXPECT_EQ(18, run3.GetInitialMax());
+    EXPECT_EQ(18, run3.GetMax());
+
+    // Make sure average decreases as well
+    max_increased = run3.Inc(5);
+    EXPECT_EQ(5, run3.Get());
+    EXPECT_EQ(18, run3.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14
+    run3.Save();
+    AssertStats(path, 4, 14);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 2 runs.
+// Data on file is 2 runs and 15 average.
+TEST_F(ProgressTest, ThirdTime) {
+    std::string path = CopyTextFileFixture("stats-two-runs.txt");
+    AssertStats(path, 2, 15);  // Sanity check
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(15, run1.GetInitialMax());
+    EXPECT_EQ(15, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16
+    run1.Save();
+    AssertStats(path, 3, 16);
+}
+
+class DumpstateUtilTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+        SetDryRun(false);
+    }
+
+    void CaptureFdOut() {
+        ReadFileToString(path_, &out);
+    }
+
+    void CreateFd(const std::string& name) {
+        path_ = kTestDataPath + name;
+        MYLOGD("Creating fd for file %s\n", path_.c_str());
+
+        fd = TEMP_FAILURE_RETRY(open(path_.c_str(),
+                                     O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+        ASSERT_GE(fd, 0) << "could not create FD for path " << path_;
+    }
+
+    // Runs a command into the `fd` and capture `stderr`.
+    int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options = CommandOptions::DEFAULT) {
+        CaptureStderr();
+        int status = RunCommandToFd(fd, title, full_command, options);
+        close(fd);
+
+        CaptureFdOut();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Dumps a file and into the `fd` and `stderr`.
+    int DumpFile(const std::string& title, const std::string& path) {
+        CaptureStderr();
+        int status = DumpFileToFd(fd, title, path);
+        close(fd);
+
+        CaptureFdOut();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Find out the pid of the process_name
+    int FindPidOfProcess(const std::string& process_name) {
+        CaptureStderr();
+        int status = GetPidByName(process_name);
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    int fd;
+
+    // 'fd` output and `stderr` from the last command ran.
+    std::string out, err;
+
+  private:
+    std::string path_;
+};
+
+TEST_F(DumpstateUtilTest, RunCommandNoArgs) {
+    CreateFd("RunCommandNoArgs.txt");
+    EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNoTitle) {
+    CreateFd("RunCommandWithNoArgs.txt");
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithTitle) {
+    CreateFd("RunCommandWithNoArgs.txt");
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithOneArg) {
+    CreateFd("RunCommandWithOneArg.txt");
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithMultipleArgs) {
+    CreateFd("RunCommandWithMultipleArgs.txt");
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithLoggingMessage) {
+    CreateFd("RunCommandWithLoggingMessage.txt");
+    EXPECT_EQ(
+        0, RunCommand("", {kSimpleCommand},
+                      CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandRedirectStderr) {
+    CreateFd("RunCommandRedirectStderr.txt");
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+                            CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRun) {
+    CreateFd("RunCommandDryRun.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq(android::base::StringPrintf(
+                         "------ I AM GROOT (%s) ------\n\t(skipped on dry run)\n",
+                         kSimpleCommand.c_str())));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunNoTitle) {
+    CreateFd("RunCommandDryRun.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(
+        out, StrEq(android::base::StringPrintf("%s: skipped on dry run\n", kSimpleCommand.c_str())));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunAlways) {
+    CreateFd("RunCommandDryRunAlways.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNotFound) {
+    CreateFd("RunCommandNotFound.txt");
+    EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+    EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+    EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandFails) {
+    CreateFd("RunCommandFails.txt");
+    EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandCrashes) {
+    CreateFd("RunCommandCrashes.txt");
+    EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+    // We don't know the exit code, so check just the prefix.
+    EXPECT_THAT(
+        out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+    EXPECT_THAT(
+        err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandTimesout) {
+    CreateFd("RunCommandTimesout.txt");
+    EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+                             CommandOptions::WithTimeout(1).Build()));
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandIsKilled) {
+    CreateFd("RunCommandIsKilled.txt");
+    CaptureStderr();
+
+    std::thread t([=]() {
+        EXPECT_EQ(SIGTERM, RunCommandToFd(fd, "", {kSimpleCommand, "--pid", "--sleep", "20"},
+                                          CommandOptions::WithTimeout(100).Always().Build()));
+    });
+
+    // Capture pid and pre-sleep output.
+    sleep(1);  // Wait a little bit to make sure pid and 1st line were printed.
+    std::string err = GetCapturedStderr();
+    EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+    CaptureFdOut();
+    std::vector<std::string> lines = android::base::Split(out, "\n");
+    ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+    int pid = atoi(lines[0].c_str());
+    EXPECT_THAT(lines[1], StrEq("stdout line1"));
+    EXPECT_THAT(lines[2], IsEmpty());  // \n
+
+    // Then kill the process.
+    CaptureFdOut();
+    CaptureStderr();
+    ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+    t.join();
+
+    // Finally, check output after murder.
+    CaptureFdOut();
+    err = GetCapturedStderr();
+
+    // out starts with the pid, which is an unknown
+    EXPECT_THAT(out, EndsWith("stdout line1\n*** command '" + kSimpleCommand +
+                              " --pid --sleep 20' failed: killed by signal 15\n"));
+    EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootUserBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootUserBuild.txt");
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    // We don't know the exact path of su, so we just check for the 'root ...' commands
+    EXPECT_THAT(out, StartsWith("Skipping"));
+    EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootNonUserBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootNonUserBuild.txt");
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandDropRoot() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandDropRoot.txt");
+    // First check root case - only available when running with 'adb root'.
+    uid_t uid = getuid();
+    if (uid == 0) {
+        EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+        EXPECT_THAT(out, StrEq("0\nstdout\n"));
+        EXPECT_THAT(err, StrEq("stderr\n"));
+        return;
+    }
+    // Then run dropping root.
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).DropRoot().Build()));
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundNoTitle) {
+    CreateFd("DumpFileNotFound.txt");
+    EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out,
+                StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundWithTitle) {
+    CreateFd("DumpFileNotFound.txt");
+    EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No such "
+                           "file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLine) {
+    CreateFd("DumpFileSingleLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLineWithNewLine) {
+    CreateFd("DumpFileSingleLineWithNewLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLines) {
+    CreateFd("DumpFileMultipleLines.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLinesWithNewLine) {
+    CreateFd("DumpFileMultipleLinesWithNewLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRunNoTitle) {
+    CreateFd("DumpFileOnDryRun.txt");
+    SetDryRun(true);
+    std::string path = kTestDataPath + "single-line.txt";
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq(path + ": skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRun) {
+    CreateFd("DumpFileOnDryRun.txt");
+    SetDryRun(true);
+    std::string path = kTestDataPath + "single-line.txt";
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(
+        out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+    EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) {
+    // init process always has pid 1.
+    EXPECT_EQ(1, FindPidOfProcess("init"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) {
+    // find the process with abnormal name.
+    EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543"));
+    EXPECT_THAT(err, StrEq("can't find the pid\n"));
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/tests/dumpstate_test_fixture.cpp b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
new file mode 100644
index 0000000..5be4719
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+void PrintDefaultOutput() {
+    fprintf(stdout, "stdout\n");
+    fflush(stdout);
+    fprintf(stderr, "stderr\n");
+    fflush(stderr);
+}
+
+/*
+ * Binary used to on RunCommand tests.
+ *
+ * Usage:
+ *
+ * - Unless stated otherwise this command:
+ *
+ *   1.Prints `stdout\n` on `stdout` and flushes it.
+ *   2.Prints `stderr\n` on `stderr` and flushes it.
+ *   3.Exit with status 0.
+ *
+ * - If 1st argument is '--pid', it first prints its pid on `stdout`.
+ *
+ * - If 1st argument is '--uid', it first prints its uid on `stdout`.
+ *
+ * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666.
+ *
+ * - With argument '--exit' 'CODE', returns CODE;
+ *
+ * - With argument '--sleep 'TIME':
+ *
+ *   1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr`
+ *   2.Sleeps for TIME s
+ *   3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr`
+ */
+int main(int argc, char* const argv[]) {
+    if (argc == 2) {
+        if (strcmp(argv[1], "--crash") == 0) {
+            PrintDefaultOutput();
+            LOG_FATAL("D'OH\n");
+            return 666;
+        }
+    }
+    if (argc == 3) {
+        if (strcmp(argv[1], "--exit") == 0) {
+            PrintDefaultOutput();
+            return atoi(argv[2]);
+        }
+    }
+
+    if (argc > 1) {
+        int index = 1;
+
+        // First check arguments that can shift the index.
+        if (strcmp(argv[1], "--pid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getpid());
+            fflush(stdout);
+        } else if (strcmp(argv[1], "--uid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getuid());
+            fflush(stdout);
+        }
+
+        // Then the "common" arguments, if any.
+        if (argc > index + 1) {
+            if (strcmp(argv[index], "--sleep") == 0) {
+                int napTime = atoi(argv[index + 1]);
+                fprintf(stdout, "stdout line1\n");
+                fflush(stdout);
+                fprintf(stderr, "sleeping for %ds\n", napTime);
+                fflush(stderr);
+                sleep(napTime);
+                fprintf(stdout, "stdout line2\n");
+                fflush(stdout);
+                fprintf(stderr, "woke up\n");
+                fflush(stderr);
+                return 0;
+            }
+        }
+    }
+
+    PrintDefaultOutput();
+    return 0;
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index af6c666..eefdcbd 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -14,43 +14,65 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "dumpstate"
+
+#include "dumpstate.h"
+
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
-#include <limits.h>
+#include <libgen.h>
+#include <math.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string>
 #include <string.h>
 #include <sys/capability.h>
 #include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
-#include <sys/sysconf.h>
 #include <sys/time.h>
 #include <sys/wait.h>
-#include <sys/klog.h>
 #include <time.h>
 #include <unistd.h>
-#include <vector>
-#include <sys/prctl.h>
 
-#define LOG_TAG "dumpstate"
+#include <set>
+#include <string>
+#include <vector>
 
 #include <android-base/file.h>
-#include <cutils/debugger.h>
-#include <cutils/log.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <debuggerd/client.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
-#include <selinux/android.h>
+#include "DumpstateInternal.h"
 
-#include "dumpstate.h"
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
 
-static const int64_t NANOS_PER_SEC = 1000000000;
+// Keep in sync with
+// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
+
+/* Most simple commands have 10 as timeout, so 5 is a good estimate */
+static const int32_t WEIGHT_FILE = 5;
+
+// TODO: temporary variables and functions used during C++ refactoring
+static Dumpstate& ds = Dumpstate::GetInstance();
+static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                      const CommandOptions& options = CommandOptions::DEFAULT) {
+    return ds.RunCommand(title, full_command, options);
+}
 
 /* list of native processes to include in the native dumps */
 // This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
@@ -58,47 +80,190 @@
         "/system/bin/audioserver",
         "/system/bin/cameraserver",
         "/system/bin/drmserver",
-        "/system/bin/mediacodec",     // media.codec
         "/system/bin/mediadrmserver",
         "/system/bin/mediaextractor", // media.extractor
         "/system/bin/mediaserver",
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
         "/system/bin/vehicle_network_service",
+        "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec
         NULL,
 };
 
-DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+/* list of hal interface to dump containing process during native dumps */
+static const char* hal_interfaces_to_dump[] {
+        "android.hardware.audio@2.0::IDevicesFactory",
+        "android.hardware.bluetooth@1.0::IBluetoothHci",
+        "android.hardware.camera.provider@2.4::ICameraProvider",
+        "android.hardware.graphics.composer@2.1::IComposer",
+        "android.hardware.vr@1.0::IVr",
+        "android.hardware.media.omx@1.0::IOmx",
+        NULL,
+};
 
-DurationReporter::DurationReporter(const char *title, FILE *out) {
-    title_ = title;
-    if (title) {
-        started_ = DurationReporter::nanotime();
+// Reasonable value for max stats.
+static const int STATS_MAX_N_RUNS = 1000;
+static const long STATS_MAX_AVERAGE = 100000;
+
+CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+
+Dumpstate::Dumpstate(const std::string& version)
+    : pid_(getpid()), version_(version), now_(time(nullptr)) {
+}
+
+Dumpstate& Dumpstate::GetInstance() {
+    static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT));
+    return singleton_;
+}
+
+DurationReporter::DurationReporter(const std::string& title, bool log_only)
+    : title_(title), log_only_(log_only) {
+    if (!title_.empty()) {
+        started_ = Nanotime();
     }
-    out_ = out;
 }
 
 DurationReporter::~DurationReporter() {
-    if (title_) {
-        uint64_t elapsed = DurationReporter::nanotime() - started_;
-        // Use "Yoda grammar" to make it easier to grep|sort sections.
-        if (out_) {
-            fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
-                   (float) elapsed / NANOS_PER_SEC, title_);
+    if (!title_.empty()) {
+        uint64_t elapsed = Nanotime() - started_;
+        if (log_only_) {
+            MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
         } else {
-            MYLOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+            // Use "Yoda grammar" to make it easier to grep|sort sections.
+            printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC,
+                   title_.c_str());
         }
     }
 }
 
-uint64_t DurationReporter::DurationReporter::nanotime() {
-    struct timespec ts;
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-    return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
+const int32_t Progress::kDefaultMax = 5000;
+
+Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) {
+}
+
+Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor)
+    : Progress(initial_max, growth_factor, "") {
+    progress_ = progress;
+}
+
+Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path)
+    : initial_max_(initial_max),
+      progress_(0),
+      max_(initial_max),
+      growth_factor_(growth_factor),
+      n_runs_(0),
+      average_max_(0),
+      path_(path) {
+    if (!path_.empty()) {
+        Load();
+    }
+}
+
+void Progress::Load() {
+    MYLOGD("Loading stats from %s\n", path_.c_str());
+    std::string content;
+    if (!android::base::ReadFileToString(path_, &content)) {
+        MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_);
+        return;
+    }
+    if (content.empty()) {
+        MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_);
+        return;
+    }
+    std::vector<std::string> lines = android::base::Split(content, "\n");
+
+    if (lines.size() < 1) {
+        MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(),
+               (int)lines.size(), max_);
+        return;
+    }
+    char* ptr;
+    n_runs_ = strtol(lines[0].c_str(), &ptr, 10);
+    average_max_ = strtol(ptr, nullptr, 10);
+    if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS ||
+        average_max_ > STATS_MAX_AVERAGE) {
+        MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str());
+        initial_max_ = Progress::kDefaultMax;
+    } else {
+        initial_max_ = average_max_;
+    }
+    max_ = initial_max_;
+
+    MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_);
+}
+
+void Progress::Save() {
+    int32_t total = n_runs_ * average_max_ + progress_;
+    int32_t runs = n_runs_ + 1;
+    int32_t average = floor(((float)total) / runs);
+    MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average,
+           path_.c_str());
+    if (path_.empty()) {
+        return;
+    }
+
+    std::string content = android::base::StringPrintf("%d %d\n", runs, average);
+    if (!android::base::WriteStringToFile(content, path_)) {
+        MYLOGE("Could not save stats on %s\n", path_.c_str());
+    }
+}
+
+int32_t Progress::Get() const {
+    return progress_;
+}
+
+bool Progress::Inc(int32_t delta) {
+    bool changed = false;
+    if (delta >= 0) {
+        progress_ += delta;
+        if (progress_ > max_) {
+            int32_t old_max = max_;
+            max_ = floor((float)progress_ * growth_factor_);
+            MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_);
+            changed = true;
+        }
+    }
+    return changed;
+}
+
+int32_t Progress::GetMax() const {
+    return max_;
+}
+
+int32_t Progress::GetInitialMax() const {
+    return initial_max_;
+}
+
+void Progress::Dump(int fd, const std::string& prefix) const {
+    const char* pr = prefix.c_str();
+    dprintf(fd, "%sprogress: %d\n", pr, progress_);
+    dprintf(fd, "%smax: %d\n", pr, max_);
+    dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_);
+    dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_);
+    dprintf(fd, "%spath: %s\n", pr, path_.c_str());
+    dprintf(fd, "%sn_runs: %d\n", pr, n_runs_);
+    dprintf(fd, "%saverage_max: %d\n", pr, average_max_);
+}
+
+bool Dumpstate::IsZipping() const {
+    return zip_writer_ != nullptr;
+}
+
+std::string Dumpstate::GetPath(const std::string& suffix) const {
+    return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
+                                       name_.c_str(), suffix.c_str());
+}
+
+void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) {
+    progress_ = std::move(progress);
 }
 
 void for_each_userid(void (*func)(int), const char *header) {
-    ON_DRY_RUN_RETURN();
+    std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf(
+                                                                    "for_each_userid(%s)", header);
+    DurationReporter duration_reporter(title);
+    if (PropertiesHelper::IsDryRun()) return;
+
     DIR *d;
     struct dirent *de;
 
@@ -180,8 +345,12 @@
 }
 
 void for_each_pid(for_each_pid_func func, const char *header) {
-    ON_DRY_RUN_RETURN();
-  __for_each_pid(for_each_pid_helper, header, (void *)func);
+    std::string title = header == nullptr ? "for_each_pid"
+                                          : android::base::StringPrintf("for_each_pid(%s)", header);
+    DurationReporter duration_reporter(title);
+    if (PropertiesHelper::IsDryRun()) return;
+
+    __for_each_pid(for_each_pid_helper, header, (void *) func);
 }
 
 static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
@@ -233,12 +402,18 @@
 }
 
 void for_each_tid(for_each_tid_func func, const char *header) {
-    ON_DRY_RUN_RETURN();
+    std::string title = header == nullptr ? "for_each_tid"
+                                          : android::base::StringPrintf("for_each_tid(%s)", header);
+    DurationReporter duration_reporter(title);
+
+    if (PropertiesHelper::IsDryRun()) return;
+
     __for_each_pid(for_each_tid_helper, header, (void *) func);
 }
 
 void show_wchan(int pid, int tid, const char *name) {
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
+
     char path[255];
     char buffer[255];
     int fd, ret, save_errno;
@@ -304,7 +479,8 @@
 }
 
 void show_showtime(int pid, const char *name) {
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
+
     char path[255];
     char buffer[1023];
     int fd, ret, save_errno;
@@ -361,7 +537,7 @@
     if (iotime) {
         snprdec(buffer, sizeof(buffer), 79, permille);
     }
-    puts(buffer); // adds a trailing newline
+    puts(buffer);  // adds a trailing newline
 
     return;
 }
@@ -371,7 +547,8 @@
     DurationReporter duration_reporter(title);
     printf("------ %s ------\n", title);
 
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
+
     /* Get size of kernel buffer */
     int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
     if (size <= 0) {
@@ -401,84 +578,17 @@
 
     snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
     snprintf(arg, sizeof(arg), "%d", pid);
-    run_command(title, 10, SU_PATH, "root", "showmap", "-q", arg, NULL);
+    RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
 }
 
-static int _dump_file_from_fd(const char *title, const char *path, int fd) {
-    if (title) {
-        printf("------ %s (%s", title, path);
-
-        struct stat st;
-        // Only show the modification time of non-device files.
-        size_t path_len = strlen(path);
-        if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
-                (path_len < 5 || memcmp(path, "/sys/", 5)) &&
-                (path_len < 3 || memcmp(path, "/d/", 3)) &&
-                !fstat(fd, &st)) {
-            char stamp[80];
-            time_t mtime = st.st_mtime;
-            strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
-            printf(": %s", stamp);
-        }
-        printf(") ------\n");
-    }
-    ON_DRY_RUN({ update_progress(WEIGHT_FILE); close(fd); return 0; });
-
-    bool newline = false;
-    fd_set read_set;
-    struct timeval tm;
-    while (1) {
-        FD_ZERO(&read_set);
-        FD_SET(fd, &read_set);
-        /* Timeout if no data is read for 30 seconds. */
-        tm.tv_sec = 30;
-        tm.tv_usec = 0;
-        uint64_t elapsed = DurationReporter::nanotime();
-        int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
-        if (ret == -1) {
-            printf("*** %s: select failed: %s\n", path, strerror(errno));
-            newline = true;
-            break;
-        } else if (ret == 0) {
-            elapsed = DurationReporter::nanotime() - elapsed;
-            printf("*** %s: Timed out after %.3fs\n", path,
-                   (float) elapsed / NANOS_PER_SEC);
-            newline = true;
-            break;
-        } else {
-            char buffer[65536];
-            ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
-            if (bytes_read > 0) {
-                fwrite(buffer, bytes_read, 1, stdout);
-                newline = (buffer[bytes_read-1] == '\n');
-            } else {
-                if (bytes_read == -1) {
-                    printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
-                    newline = true;
-                }
-                break;
-            }
-        }
-    }
-    update_progress(WEIGHT_FILE);
-    close(fd);
-
-    if (!newline) printf("\n");
-    if (title) printf("\n");
-    return 0;
-}
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char *path) {
+int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
     DurationReporter duration_reporter(title);
-    int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
-    if (fd < 0) {
-        int err = errno;
-        printf("*** %s: %s\n", path, strerror(err));
-        if (title) printf("\n");
-        return -1;
-    }
-    return _dump_file_from_fd(title, path, fd);
+
+    int status = DumpFileToFd(STDOUT_FILENO, title, path);
+
+    UpdateProgress(WEIGHT_FILE);
+
+    return status;
 }
 
 int read_file_as_long(const char *path, long int *output) {
@@ -508,9 +618,8 @@
  * to false when set to NULL. dump_from_fd will always be
  * called with title NULL.
  */
-int dump_files(const char *title, const char *dir,
-        bool (*skip)(const char *path),
-        int (*dump_from_fd)(const char *title, const char *path, int fd)) {
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+               int (*dump_from_fd)(const char* title, const char* path, int fd)) {
     DurationReporter duration_reporter(title);
     DIR *dirp;
     struct dirent *d;
@@ -518,10 +627,10 @@
     const char *slash = "/";
     int fd, retval = 0;
 
-    if (title) {
-        printf("------ %s (%s) ------\n", title, dir);
+    if (!title.empty()) {
+        printf("------ %s (%s) ------\n", title.c_str(), dir);
     }
-    ON_DRY_RUN_RETURN(0);
+    if (PropertiesHelper::IsDryRun()) return 0;
 
     if (dir[strlen(dir) - 1] == '/') {
         ++slash;
@@ -552,7 +661,7 @@
             continue;
         }
         if (d->d_type == DT_DIR) {
-            int ret = dump_files(NULL, newpath, skip, dump_from_fd);
+            int ret = dump_files("", newpath, skip, dump_from_fd);
             if (ret < 0) {
                 retval = ret;
             }
@@ -567,7 +676,7 @@
         (*dump_from_fd)(NULL, newpath, fd);
     }
     closedir(dirp);
-    if (title) {
+    if (!title.empty()) {
         printf("\n");
     }
     return retval;
@@ -578,6 +687,8 @@
  * stuck.
  */
 int dump_file_from_fd(const char *title, const char *path, int fd) {
+    if (PropertiesHelper::IsDryRun()) return 0;
+
     int flags = fcntl(fd, F_GETFL);
     if (flags == -1) {
         printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
@@ -588,332 +699,30 @@
         close(fd);
         return -1;
     }
-    return _dump_file_from_fd(title, path, fd);
+    return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
 }
 
-bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
-    sigset_t child_mask, old_mask;
-    sigemptyset(&child_mask);
-    sigaddset(&child_mask, SIGCHLD);
-
-    if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
-        printf("*** sigprocmask failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    struct timespec ts;
-    ts.tv_sec = timeout_seconds;
-    ts.tv_nsec = 0;
-    int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
-    int saved_errno = errno;
-    // Set the signals back the way they were.
-    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
-        printf("*** sigprocmask failed: %s\n", strerror(errno));
-        if (ret == 0) {
-            return false;
-        }
-    }
-    if (ret == -1) {
-        errno = saved_errno;
-        if (errno == EAGAIN) {
-            errno = ETIMEDOUT;
-        } else {
-            printf("*** sigtimedwait failed: %s\n", strerror(errno));
-        }
-        return false;
-    }
-
-    pid_t child_pid = waitpid(pid, status, WNOHANG);
-    if (child_pid != pid) {
-        if (child_pid != -1) {
-            printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
-        } else {
-            printf("*** waitpid failed: %s\n", strerror(errno));
-        }
-        return false;
-    }
-    return true;
-}
-
-// TODO: refactor all those commands that convert args
-void format_args(const char* command, const char *args[], std::string *string);
-
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                          const CommandOptions& options) {
     DurationReporter duration_reporter(title);
-    fflush(stdout);
 
-    const char *args[1024] = {command};
-    size_t arg;
-    va_list ap;
-    va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
-            break;
-        }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
-    }
-    if (title) printf(") ------\n");
-    fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        return -1;
-    }
+    int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
 
-    ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+    /* TODO: for now we're simplifying the progress calculation by using the
+     * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+     * where its weight should be much higher proportionally to its timeout.
+     * Ideally, it should use a options.EstimatedDuration() instead...*/
+    UpdateProgress(options.Timeout());
 
-    int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
-    va_end(ap);
     return status;
 }
 
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...) {
-    DurationReporter duration_reporter(title);
-    fflush(stdout);
-
-    const char *args[1024] = {command};
-    size_t arg;
-    va_list ap;
-    va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
-            break;
-        }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
-    }
-    if (title) printf(") ------\n");
-    fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        return -1;
-    }
-
-    ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
-
-    int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
-    va_end(ap);
-    return status;
-}
-
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]) {
-    bool silent = (stdout_mode == REDIRECT_TO_STDERR);
-    // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
-
-    /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
-     * It's a good approximation for most cases, except when calling dumpsys, where its weight
-     * should be much higher proportionally to its timeout. */
-    int weight = timeout_seconds;
-
-    const char *command = args[0];
-    uint64_t start = DurationReporter::nanotime();
-    pid_t pid = fork();
-
-    /* handle error case */
-    if (pid < 0) {
-        if (!silent) printf("*** fork: %s\n", strerror(errno));
-        MYLOGE("*** fork: %s\n", strerror(errno));
-        return pid;
-    }
-
-    /* handle child case */
-    if (pid == 0) {
-        if (root_mode == DROP_ROOT && !drop_root_user()) {
-        if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
-                strerror(errno));
-            MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
-            return -1;
-        }
-
-        if (silent) {
-            // Redirect stderr to stdout
-            dup2(STDERR_FILENO, STDOUT_FILENO);
-        }
-
-        /* make sure the child dies when dumpstate dies */
-        prctl(PR_SET_PDEATHSIG, SIGKILL);
-
-        /* just ignore SIGPIPE, will go down with parent's */
-        struct sigaction sigact;
-        memset(&sigact, 0, sizeof(sigact));
-        sigact.sa_handler = SIG_IGN;
-        sigaction(SIGPIPE, &sigact, NULL);
-
-        execvp(command, (char**) args);
-        // execvp's result will be handled after waitpid_with_timeout() below, but if it failed,
-        // it's safer to exit dumpstate.
-        MYLOGD("execvp on command '%s' failed (error: %s)", command, strerror(errno));
-        fflush(stdout);
-        // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
-        _exit(EXIT_FAILURE);
-    }
-
-    /* handle parent case */
-    int status;
-    bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
-    uint64_t elapsed = DurationReporter::nanotime() - start;
-    std::string cmd; // used to log command and its args
-    if (!ret) {
-        if (errno == ETIMEDOUT) {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
-        } else {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
-        }
-        kill(pid, SIGTERM);
-        if (!waitpid_with_timeout(pid, 5, NULL)) {
-            kill(pid, SIGKILL);
-            if (!waitpid_with_timeout(pid, 5, NULL)) {
-                if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
-                        command, pid);
-                MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
-            }
-        }
-        return -1;
-    } else if (status) {
-        format_args(command, args, &cmd);
-        if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        return -2;
-    }
-
-    if (WIFSIGNALED(status)) {
-        if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
-        MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
-    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-        if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
-        MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
-    }
-
-    if (weight > 0) {
-        update_progress(weight);
-    }
-    return status;
-}
-
-bool drop_root_user() {
-    if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
-        MYLOGD("drop_root_user(): already running as Shell");
-        return true;
-    }
-    /* ensure we will keep capabilities when we drop root */
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
-        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) {
-        MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
-        return false;
-    }
-    if (setgid(AID_SHELL) != 0) {
-        MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
-        return false;
-    }
-    if (setuid(AID_SHELL) != 0) {
-        MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
-        return false;
-    }
-
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[0].inheritable = 0;
-    capdata[1].inheritable = 0;
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        MYLOGE("capset failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    return true;
-}
-
-void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
-    if (args.size() > 1000) {
-        MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
-        return;
-    }
-    const char *am_args[1024] = { "/system/bin/am", "broadcast", "--user", "0", "-a",
-                                  action.c_str() };
-    size_t am_index = 5; // Starts at the index of last initial value above.
-    for (const std::string& arg : args) {
-        am_args[++am_index] = arg.c_str();
-    }
-    // Always terminate with NULL.
-    am_args[am_index + 1] = NULL;
-    std::string args_string;
-    format_args(am_index + 1, am_args, &args_string);
-    MYLOGD("send_broadcast command: %s\n", args_string.c_str());
-    run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
-}
-
-size_t num_props = 0;
-static char* props[2000];
-
-static void print_prop(const char *key, const char *name, void *user) {
-    (void) user;
-    if (num_props < sizeof(props) / sizeof(props[0])) {
-        char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
-        snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
-        props[num_props++] = strdup(buf);
-    }
-}
-
-static int compare_prop(const void *a, const void *b) {
-    return strcmp(*(char * const *) a, *(char * const *) b);
-}
-
-/* prints all the system properties */
-void print_properties() {
-    const char* title = "SYSTEM PROPERTIES";
-    DurationReporter duration_reporter(title);
-    printf("------ %s ------\n", title);
-    ON_DRY_RUN_RETURN();
-    size_t i;
-    num_props = 0;
-    property_list(print_prop, NULL);
-    qsort(&props, num_props, sizeof(props[0]), compare_prop);
-
-    for (i = 0; i < num_props; ++i) {
-        fputs(props[i], stdout);
-        free(props[i]);
-    }
-    printf("\n");
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+                           const CommandOptions& options, long dumpsysTimeout) {
+    long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
+    std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
+    dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
+    RunCommand(title, dumpsys, options);
 }
 
 int open_socket(const char *service) {
@@ -974,11 +783,11 @@
     }
 }
 
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
+void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
     create_parent_dirs(path);
 
-    int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+    int fd = TEMP_FAILURE_RETRY(open(path,
+                                     O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW,
                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     if (fd < 0) {
         MYLOGE("%s: %s\n", path, strerror(errno));
@@ -989,6 +798,23 @@
     close(fd);
 }
 
+void redirect_to_file(FILE *redirect, char *path) {
+    _redirect_to_file(redirect, path, O_TRUNC);
+}
+
+void redirect_to_existing_file(FILE *redirect, char *path) {
+    _redirect_to_file(redirect, path, O_APPEND);
+}
+
+static bool should_dump_hal_interface(const char* interface) {
+    for (const char** i = hal_interfaces_to_dump; *i; i++) {
+        if (!strcmp(*i, interface)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 static bool should_dump_native_traces(const char* path) {
     for (const char** p = native_processes_to_dump; *p; p++) {
         if (!strcmp(*p, path)) {
@@ -998,42 +824,70 @@
     return false;
 }
 
+std::set<int> get_interesting_hal_pids() {
+    using android::hidl::manager::V1_0::IServiceManager;
+    using android::sp;
+    using android::hardware::Return;
+
+    sp<IServiceManager> manager = IServiceManager::getService();
+    std::set<int> pids;
+
+    Return<void> ret = manager->debugDump([&](auto& hals) {
+        for (const auto &info : hals) {
+            if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
+                continue;
+            }
+
+            if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+                continue;
+            }
+
+            pids.insert(info.pid);
+        }
+    });
+
+    if (!ret.isOk()) {
+        MYLOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str());
+    }
+
+    return pids; // whether it was okay or not
+}
+
 /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
 const char *dump_traces() {
-    DurationReporter duration_reporter("DUMP TRACES", NULL);
-    ON_DRY_RUN_RETURN(NULL);
-    const char* result = NULL;
+    DurationReporter duration_reporter("DUMP TRACES");
 
-    char traces_path[PROPERTY_VALUE_MAX] = "";
-    property_get("dalvik.vm.stack-trace-file", traces_path, "");
-    if (!traces_path[0]) return NULL;
+    const char* result = nullptr;
+
+    std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    if (traces_path.empty()) return nullptr;
 
     /* move the old traces.txt (if any) out of the way temporarily */
-    char anr_traces_path[PATH_MAX];
-    strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
-    strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
-    if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
-        MYLOGE("rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
-        return NULL;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
+    std::string anrtraces_path = traces_path + ".anr";
+    if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+        MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
+        return nullptr;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
     }
 
     /* create a new, empty traces.txt file to receive stack dumps */
-    int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
-                                     0666));  /* -rw-rw-rw- */
+    int fd = TEMP_FAILURE_RETRY(
+        open(traces_path.c_str(), O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
+             0666)); /* -rw-rw-rw- */
     if (fd < 0) {
-        MYLOGE("%s: %s\n", traces_path, strerror(errno));
-        return NULL;
+        MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
+        return nullptr;
     }
     int chmod_ret = fchmod(fd, 0666);
     if (chmod_ret < 0) {
-        MYLOGE("fchmod on %s failed: %s\n", traces_path, strerror(errno));
+        MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
         close(fd);
-        return NULL;
+        return nullptr;
     }
 
     /* Variables below must be initialized before 'goto' statements */
     int dalvik_found = 0;
     int ifd, wfd = -1;
+    std::set<int> hal_pids = get_interesting_hal_pids();
 
     /* walk /proc and kill -QUIT all Dalvik processes */
     DIR *proc = opendir("/proc");
@@ -1049,9 +903,9 @@
         goto error_close_fd;
     }
 
-    wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+    wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
     if (wfd < 0) {
-        MYLOGE("inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+        MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
         goto error_close_ifd;
     }
 
@@ -1084,7 +938,7 @@
             }
 
             ++dalvik_found;
-            uint64_t start = DurationReporter::nanotime();
+            uint64_t start = Nanotime();
             if (kill(pid, SIGQUIT)) {
                 MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
                 continue;
@@ -1092,7 +946,7 @@
 
             /* wait for the writable-close notification from inotify */
             struct pollfd pfd = { ifd, POLLIN, 0 };
-            int ret = poll(&pfd, 1, 5000);  /* 5 sec timeout */
+            int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS);
             if (ret < 0) {
                 MYLOGE("poll: %s\n", strerror(errno));
             } else if (ret == 0) {
@@ -1105,16 +959,17 @@
             if (lseek(fd, 0, SEEK_END) < 0) {
                 MYLOGE("lseek: %s\n", strerror(errno));
             } else {
-                dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n",
-                        pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+                dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n", pid,
+                        (float)(Nanotime() - start) / NANOS_PER_SEC);
             }
-        } else if (should_dump_native_traces(data)) {
+        } else if (should_dump_native_traces(data) ||
+                   hal_pids.find(pid) != hal_pids.end()) {
             /* dump native process if appropriate */
             if (lseek(fd, 0, SEEK_END) < 0) {
                 MYLOGE("lseek: %s\n", strerror(errno));
             } else {
                 static uint16_t timeout_failures = 0;
-                uint64_t start = DurationReporter::nanotime();
+                uint64_t start = Nanotime();
 
                 /* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
                 if (timeout_failures == 3) {
@@ -1125,8 +980,8 @@
                 } else {
                     timeout_failures = 0;
                 }
-                dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
-                        pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+                dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid,
+                        (float)(Nanotime() - start) / NANOS_PER_SEC);
             }
         }
     }
@@ -1135,17 +990,17 @@
         MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
     }
 
-    static char dump_traces_path[PATH_MAX];
-    strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
-    strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
-    if (rename(traces_path, dump_traces_path)) {
-        MYLOGE("rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+    static std::string dumptraces_path = android::base::StringPrintf(
+        "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+    if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+        MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+               strerror(errno));
         goto error_close_ifd;
     }
-    result = dump_traces_path;
+    result = dumptraces_path.c_str();
 
     /* replace the saved [ANR] traces.txt file */
-    rename(anr_traces_path, traces_path);
+    rename(anrtraces_path.c_str(), traces_path.c_str());
 
 error_close_ifd:
     close(ifd);
@@ -1156,9 +1011,9 @@
 
 void dump_route_tables() {
     DurationReporter duration_reporter("DUMP ROUTE TABLES");
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
     const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
-    dump_file("RT_TABLES", RT_TABLES_PATH);
+    ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
     FILE* fp = fopen(RT_TABLES_PATH, "re");
     if (!fp) {
         printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
@@ -1169,72 +1024,67 @@
     // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
     // Add a fixed max limit so this doesn't go awry.
     for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
-        run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
-        run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
+        RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table});
+        RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table});
     }
     fclose(fp);
 }
 
-/* overall progress */
-int progress = 0;
-int do_update_progress = 0; // Set by dumpstate.cpp
-int weight_total = WEIGHT_TOTAL;
-
 // TODO: make this function thread safe if sections are generated in parallel.
-void update_progress(int delta) {
-    if (!do_update_progress) return;
+void Dumpstate::UpdateProgress(int32_t delta) {
+    if (progress_ == nullptr) {
+        MYLOGE("UpdateProgress: progress_ not set\n");
+        return;
+    }
 
-    progress += delta;
+    // Always update progess so stats can be tuned...
+    bool max_changed = progress_->Inc(delta);
 
-    char key[PROPERTY_KEY_MAX];
-    char value[PROPERTY_VALUE_MAX];
+    // ...but only notifiy listeners when necessary.
+    if (!update_progress_) return;
+
+    int progress = progress_->Get();
+    int max = progress_->GetMax();
 
     // adjusts max on the fly
-    if (progress > weight_total) {
-        int new_total = weight_total * 1.2;
-        MYLOGD("Adjusting total weight from %d to %d\n", weight_total, new_total);
-        weight_total = new_total;
-        snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
-        snprintf(value, sizeof(value), "%d", weight_total);
-        int status = property_set(key, value);
-        if (status) {
-            MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
-                    key, value, status);
+    if (max_changed && listener_ != nullptr) {
+        listener_->onMaxProgressUpdated(max);
+    }
+
+    int32_t last_update_delta = progress - last_updated_progress_;
+    if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) {
+        return;
+    }
+    last_updated_progress_ = progress;
+
+    if (control_socket_fd_ >= 0) {
+        dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max);
+        fsync(control_socket_fd_);
+    }
+
+    if (listener_ != nullptr) {
+        if (progress % 100 == 0) {
+            // We don't want to spam logcat, so only log multiples of 100.
+            MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+        } else {
+            // stderr is ignored on normal invocations, but useful when calling
+            // /system/bin/dumpstate directly for debuggging.
+            fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
         }
+        listener_->onProgressUpdated(progress);
     }
+}
 
-    snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
-    snprintf(value, sizeof(value), "%d", progress);
-
-    if (progress % 100 == 0) {
-        // We don't want to spam logcat, so only log multiples of 100.
-        MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total);
+void Dumpstate::TakeScreenshot(const std::string& path) {
+    const std::string& real_path = path.empty() ? screenshot_path_ : path;
+    int status =
+        RunCommand("", {"/system/bin/screencap", "-p", real_path},
+                   CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+    if (status == 0) {
+        MYLOGD("Screenshot saved on %s\n", real_path.c_str());
     } else {
-        // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
-        // directly for debuggging.
-        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+        MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
     }
-
-    if (control_socket_fd >= 0) {
-        dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
-        fsync(control_socket_fd);
-    }
-
-    int status = property_set(key, value);
-    if (status) {
-        MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
-                key, value, status);
-    }
-}
-
-void take_screenshot(const std::string& path) {
-    const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
-    run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
-}
-
-void vibrate(FILE* vibrator, int ms) {
-    fprintf(vibrator, "%d\n", ms);
-    fflush(vibrator);
 }
 
 bool is_dir(const char* pathname) {
@@ -1278,19 +1128,16 @@
     int ext_csd_rev = 0;
     std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex));
     if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) {
-        printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n",
-               ext_csd_path, sub.c_str());
+        printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
         return;
     }
 
     static const char *ver_str[] = {
         "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
     };
-    printf("rev 1.%d (MMC %s)\n",
-           ext_csd_rev,
-           (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
-               ver_str[ext_csd_rev] :
-               "Unknown");
+    printf("rev 1.%d (MMC %s)\n", ext_csd_rev,
+           (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev]
+                                                                       : "Unknown");
     if (ext_csd_rev < 7) {
         printf("\n");
         return;
@@ -1304,8 +1151,7 @@
     int ext_pre_eol_info = 0;
     sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex));
     if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) {
-        printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n",
-               ext_csd_path, sub.c_str());
+        printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
         return;
     }
 
@@ -1315,11 +1161,10 @@
         "Warning (consumed 80% of reserve)",
         "Urgent (consumed 90% of reserve)"
     };
-    printf("PRE_EOL_INFO %d (MMC %s)\n",
-           ext_pre_eol_info,
-           eol_str[(ext_pre_eol_info < (int)
-                       (sizeof(eol_str) / sizeof(eol_str[0]))) ?
-                           ext_pre_eol_info : 0]);
+    printf(
+        "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info,
+        eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info
+                                                                                 : 0]);
 
     for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
             lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
@@ -1348,48 +1193,18 @@
         ext_device_life_time_est = 0;
         sub = buffer.substr(lifetime, sizeof(hex));
         if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) {
-            printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n",
-                   ext_csd_path,
-                   (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
-                              sizeof(hex)) + 'A',
+            printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path,
+                   (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
                    sub.c_str());
             continue;
         }
         printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n",
-               (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) /
-                          sizeof(hex)) + 'A',
+               (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A',
                ext_device_life_time_est,
-               est_str[(ext_device_life_time_est < (int)
-                           (sizeof(est_str) / sizeof(est_str[0]))) ?
-                               ext_device_life_time_est : 0]);
+               est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0])))
+                           ? ext_device_life_time_est
+                           : 0]);
     }
 
     printf("\n");
 }
-
-// TODO: refactor all those commands that convert args
-void format_args(int argc, const char *argv[], std::string *args) {
-    LOG_ALWAYS_FATAL_IF(args == nullptr);
-    for (int i = 0; i < argc; i++) {
-        args->append(argv[i]);
-        if (i < argc -1) {
-          args->append(" ");
-        }
-    }
-}
-void format_args(const char* command, const char *args[], std::string *string) {
-    LOG_ALWAYS_FATAL_IF(args == nullptr || command == nullptr);
-    string->append(command);
-    if (args[0] == nullptr) return;
-    string->append(" ");
-
-    for (int arg = 1; arg <= 1000; ++arg) {
-        if (args[arg] == nullptr) return;
-        string->append(args[arg]);
-        if (args[arg+1] != nullptr) {
-            string->append(" ");
-        }
-    }
-    // TODO: not really working: if NULL is missing, it will crash dumpstate.
-    MYLOGE("internal error: missing NULL entry on %s", string->c_str());
-}
diff --git a/cmds/dumpsys/.clang-format b/cmds/dumpsys/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpsys/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
new file mode 100644
index 0000000..3476964
--- /dev/null
+++ b/cmds/dumpsys/Android.bp
@@ -0,0 +1,50 @@
+cc_defaults {
+    name: "dumpsys_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: [
+        "dumpsys.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libutils",
+        "liblog",
+        "libbinder",
+    ],
+
+    clang: true,
+}
+
+//
+// Static library used in testing and executable
+//
+
+cc_library_static {
+    name: "libdumpsys",
+
+    defaults: ["dumpsys_defaults"],
+
+    export_include_dirs: ["."],
+}
+
+
+//
+// Executable
+//
+
+cc_binary {
+    name: "dumpsys",
+
+    defaults: ["dumpsys_defaults"],
+
+    srcs: [
+        "main.cpp",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
deleted file mode 100644
index 8335c14..0000000
--- a/cmds/dumpsys/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	dumpsys.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libbase \
-	libutils \
-	liblog \
-	libbinder
-
-
-ifeq ($(TARGET_OS),linux)
-	LOCAL_CFLAGS += -DXP_UNIX
-	#LOCAL_SHARED_LIBRARIES += librt
-endif
-
-LOCAL_MODULE:= dumpsys
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index d19e98a..f0e7200 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -1,10 +1,19 @@
 /*
- * Command that dumps interesting system state to the log.
+ * Copyright (C) 2009 The Android Open Source Project
  *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
-#define LOG_TAG "dumpsys"
-
 #include <algorithm>
 #include <chrono>
 #include <thread>
@@ -12,7 +21,6 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 #include <binder/TextOutput.h>
@@ -30,6 +38,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "dumpsys.h"
+
 using namespace android;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -53,7 +63,7 @@
             "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
 }
 
-bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
+static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
     for (const auto& candidate : skipped) {
         if (candidate == service) {
             return true;
@@ -62,17 +72,7 @@
     return false;
 }
 
-int main(int argc, char* const argv[])
-{
-    signal(SIGPIPE, SIG_IGN);
-    sp<IServiceManager> sm = defaultServiceManager();
-    fflush(stdout);
-    if (sm == NULL) {
-        ALOGE("Unable to get default service manager!");
-        aerr << "dumpsys: Unable to get default service manager!" << endl;
-        return 20;
-    }
-
+int Dumpsys::main(int argc, char* const argv[]) {
     Vector<String16> services;
     Vector<String16> args;
     Vector<String16> skippedServices;
@@ -85,6 +85,9 @@
         {     0,           0, 0,  0 }
     };
 
+    // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
+    // happens on test cases).
+    optind = 1;
     while (1) {
         int c;
         int optionIndex = 0;
@@ -147,7 +150,7 @@
 
     if (services.empty() || showListOnly) {
         // gets all services
-        services = sm->listServices();
+        services = sm_->listServices();
         services.sort(sort_func);
         args.add(String16("-a"));
     }
@@ -159,8 +162,9 @@
         aout << "Currently running services:" << endl;
 
         for (size_t i=0; i<N; i++) {
-            sp<IBinder> service = sm->checkService(services[i]);
-            if (service != NULL) {
+            sp<IBinder> service = sm_->checkService(services[i]);
+
+            if (service != nullptr) {
                 bool skipped = IsSkipped(skippedServices, services[i]);
                 aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
             }
@@ -175,8 +179,8 @@
         String16 service_name = std::move(services[i]);
         if (IsSkipped(skippedServices, service_name)) continue;
 
-        sp<IBinder> service = sm->checkService(service_name);
-        if (service != NULL) {
+        sp<IBinder> service = sm_->checkService(service_name);
+        if (service != nullptr) {
             int sfd[2];
 
             if (pipe(sfd) != 0) {
@@ -203,7 +207,7 @@
                 // call returns, to terminate our reads if the other end closes their copy of the
                 // file descriptor, but then hangs for some reason. There doesn't seem to be a good
                 // way to do this, though.
-                remote_end.clear();
+                remote_end.reset();
 
                 if (err != 0) {
                     aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
@@ -262,7 +266,10 @@
             }
 
             if (timed_out) {
-                aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
+                aout << endl
+                     << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArg
+                     << "s) EXPIRED ***" << endl
+                     << endl;
             }
 
             if (timed_out || error) {
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
new file mode 100644
index 0000000..2534dde
--- /dev/null
+++ b/cmds/dumpsys/dumpsys.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class Dumpsys {
+  public:
+    Dumpsys(android::IServiceManager* sm) : sm_(sm) {
+    }
+    int main(int argc, char* const argv[]);
+
+  private:
+    android::IServiceManager* sm_;
+};
+}
+
+#endif  // FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
new file mode 100644
index 0000000..8ba0eba
--- /dev/null
+++ b/cmds/dumpsys/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Command that dumps interesting system state to the log.
+ */
+
+#include "dumpsys.h"
+
+#include <binder/IServiceManager.h>
+#include <binder/TextOutput.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+using namespace android;
+
+int main(int argc, char* const argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+    sp<IServiceManager> sm = defaultServiceManager();
+    fflush(stdout);
+    if (sm == nullptr) {
+        ALOGE("Unable to get default service manager!");
+        aerr << "dumpsys: Unable to get default service manager!" << endl;
+        return 20;
+    }
+
+    Dumpsys dumpsys(sm.get());
+    return dumpsys.main(argc, argv);
+}
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
new file mode 100644
index 0000000..7698ed5
--- /dev/null
+++ b/cmds/dumpsys/tests/Android.bp
@@ -0,0 +1,19 @@
+// Build the unit tests for dumpsys
+cc_test {
+    name: "dumpsys_test",
+
+    srcs: ["dumpsys_test.cpp"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libdumpsys",
+        "libgmock",
+    ],
+
+    clang: true,
+}
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
new file mode 100644
index 0000000..66beb6d
--- /dev/null
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../dumpsys.h"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::MakeAction;
+using ::testing::Not;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::WithArg;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class ServiceManagerMock : public IServiceManager {
+  public:
+    MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
+    MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&));
+    MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool));
+    MOCK_METHOD0(listServices, Vector<String16>());
+
+  protected:
+    MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+class BinderMock : public BBinder {
+  public:
+    BinderMock() {
+    }
+
+    MOCK_METHOD2(dump, status_t(int, const Vector<String16>&));
+};
+
+// gmock black magic to provide a WithArg<0>(WriteOnFd(output)) matcher
+typedef void WriteOnFdFunction(int);
+
+class WriteOnFdAction : public ActionInterface<WriteOnFdFunction> {
+  public:
+    explicit WriteOnFdAction(const std::string& output) : output_(output) {
+    }
+    virtual Result Perform(const ArgumentTuple& args) {
+        int fd = ::std::tr1::get<0>(args);
+        android::base::WriteStringToFd(output_, fd);
+    }
+
+  private:
+    std::string output_;
+};
+
+// Matcher used to emulate dump() by writing on its file descriptor.
+Action<WriteOnFdFunction> WriteOnFd(const std::string& output) {
+    return MakeAction(new WriteOnFdAction(output));
+}
+
+// Matcher for args using Android's Vector<String16> format
+// TODO: move it to some common testing library
+MATCHER_P(AndroidElementsAre, expected, "") {
+    std::ostringstream errors;
+    if (arg.size() != expected.size()) {
+        errors << " sizes do not match (expected " << expected.size() << ", got " << arg.size()
+               << ")\n";
+    }
+    int i = 0;
+    std::ostringstream actual_stream, expected_stream;
+    for (String16 actual : arg) {
+        std::string actual_str = String8(actual).c_str();
+        std::string expected_str = expected[i];
+        actual_stream << "'" << actual_str << "' ";
+        expected_stream << "'" << expected_str << "' ";
+        if (actual_str != expected_str) {
+            errors << " element mismatch at index " << i << "\n";
+        }
+        i++;
+    }
+
+    if (!errors.str().empty()) {
+        errors << "\nExpected args: " << expected_stream.str()
+               << "\nActual args: " << actual_stream.str();
+        *result_listener << errors.str();
+        return false;
+    }
+    return true;
+}
+
+// Custom action to sleep for timeout seconds
+ACTION_P(Sleep, timeout) {
+    sleep(timeout);
+}
+
+class DumpsysTest : public Test {
+  public:
+    DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() {
+    }
+
+    void ExpectListServices(std::vector<std::string> services) {
+        Vector<String16> services16;
+        for (auto& service : services) {
+            services16.add(String16(service.c_str()));
+        }
+        EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
+    }
+
+    sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
+        sp<BinderMock> binder_mock;
+        if (running) {
+            binder_mock = new BinderMock;
+        }
+        EXPECT_CALL(sm_, checkService(String16(name))).WillRepeatedly(Return(binder_mock));
+        return binder_mock;
+    }
+
+    void ExpectDump(const char* name, const std::string& output) {
+        sp<BinderMock> binder_mock = ExpectCheckService(name);
+        EXPECT_CALL(*binder_mock, dump(_, _))
+            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+    }
+
+    void ExpectDumpWithArgs(const char* name, std::vector<std::string> args,
+                            const std::string& output) {
+        sp<BinderMock> binder_mock = ExpectCheckService(name);
+        EXPECT_CALL(*binder_mock, dump(_, AndroidElementsAre(args)))
+            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+    }
+
+    void 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)));
+    }
+
+    void CallMain(const std::vector<std::string>& args) {
+        const char* argv[1024] = {"/some/virtual/dir/dumpsys"};
+        int argc = (int)args.size() + 1;
+        int i = 1;
+        for (const std::string& arg : args) {
+            argv[i++] = arg.c_str();
+        }
+        CaptureStdout();
+        CaptureStderr();
+        int status = dump_.main(argc, const_cast<char**>(argv));
+        stdout_ = GetCapturedStdout();
+        stderr_ = GetCapturedStderr();
+        EXPECT_THAT(status, Eq(0));
+    }
+
+    void AssertRunningServices(const std::vector<std::string>& services) {
+        std::string expected("Currently running services:\n");
+        for (const std::string& service : services) {
+            expected.append("  ").append(service).append("\n");
+        }
+        EXPECT_THAT(stdout_, HasSubstr(expected));
+    }
+
+    void AssertOutput(const std::string& expected) {
+        EXPECT_THAT(stdout_, StrEq(expected));
+    }
+
+    void AssertOutputContains(const std::string& expected) {
+        EXPECT_THAT(stdout_, HasSubstr(expected));
+    }
+
+    void AssertDumped(const std::string& service, const std::string& dump) {
+        EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+    }
+
+    void AssertNotDumped(const std::string& dump) {
+        EXPECT_THAT(stdout_, Not(HasSubstr(dump)));
+    }
+
+    void AssertStopped(const std::string& service) {
+        EXPECT_THAT(stderr_, HasSubstr("Can't find service: " + service + "\n"));
+    }
+
+    ServiceManagerMock sm_;
+    Dumpsys dump_;
+
+  private:
+    std::string stdout_, stderr_;
+};
+
+// Tests 'dumpsys -l' when all services are running
+TEST_F(DumpsysTest, ListAllServices) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet");
+
+    CallMain({"-l"});
+
+    AssertRunningServices({"Locksmith", "Valet"});
+}
+
+// Tests 'dumpsys -l' when a service is not running
+TEST_F(DumpsysTest, ListRunningServices) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet", false);
+
+    CallMain({"-l"});
+
+    AssertRunningServices({"Locksmith"});
+    AssertNotDumped({"Valet"});
+}
+
+// Tests 'dumpsys service_name' on a service is running
+TEST_F(DumpsysTest, DumpRunningService) {
+    ExpectDump("Valet", "Here's your car");
+
+    CallMain({"Valet"});
+
+    AssertOutput("Here's your car");
+}
+
+// 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");
+
+    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);
+}
+
+// Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
+TEST_F(DumpsysTest, DumpWithArgsRunningService) {
+    ExpectDumpWithArgs("SERVICE", {"Y", "U", "NO", "HANDLE", "ARGS"}, "I DO!");
+
+    CallMain({"SERVICE", "Y", "U", "NO", "HANDLE", "ARGS"});
+
+    AssertOutput("I DO!");
+}
+
+// Tests 'dumpsys' with no arguments
+TEST_F(DumpsysTest, DumpMultipleServices) {
+    ExpectListServices({"running1", "stopped2", "running3"});
+    ExpectDump("running1", "dump1");
+    ExpectCheckService("stopped2", false);
+    ExpectDump("running3", "dump3");
+
+    CallMain({});
+
+    AssertRunningServices({"running1", "running3"});
+    AssertDumped("running1", "dump1");
+    AssertStopped("stopped2");
+    AssertDumped("running3", "dump3");
+}
+
+// Tests 'dumpsys --skip skipped3 skipped5', which should skip these services
+TEST_F(DumpsysTest, DumpWithSkip) {
+    ExpectListServices({"running1", "stopped2", "skipped3", "running4", "skipped5"});
+    ExpectDump("running1", "dump1");
+    ExpectCheckService("stopped2", false);
+    ExpectDump("skipped3", "dump3");
+    ExpectDump("running4", "dump4");
+    ExpectDump("skipped5", "dump5");
+
+    CallMain({"--skip", "skipped3", "skipped5"});
+
+    AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
+    AssertDumped("running1", "dump1");
+    AssertDumped("running4", "dump4");
+    AssertStopped("stopped2");
+    AssertNotDumped("dump3");
+    AssertNotDumped("dump5");
+}
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index ddf3aa8..dfc3e58 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -25,7 +25,6 @@
  namespace android {
 
 GLHelper::GLHelper() :
-    mGraphicBufferAlloc(new GraphicBufferAlloc()),
     mDisplay(EGL_NO_DISPLAY),
     mContext(EGL_NO_CONTEXT),
     mDummySurface(EGL_NO_SURFACE),
@@ -203,7 +202,7 @@
         sp<GLConsumer>* glConsumer, EGLSurface* surface) {
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer, mGraphicBufferAlloc);
+    BufferQueue::createBufferQueue(&producer, &consumer);
     sp<GLConsumer> glc = new GLConsumer(consumer, name,
             GL_TEXTURE_EXTERNAL_OES, false, true);
     glc->setDefaultBufferSize(w, h);
@@ -365,6 +364,7 @@
     if (!result) {
         fprintf(stderr, "Shader source:\n");
         printShaderSource(lines);
+        delete[] src;
         return false;
     }
     delete[] src;
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
index 7a9e9e3..d09463a 100644
--- a/cmds/flatland/GLHelper.h
+++ b/cmds/flatland/GLHelper.h
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <gui/GraphicBufferAlloc.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceControl.h>
@@ -75,8 +74,6 @@
 
     bool setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders);
 
-    sp<GraphicBufferAlloc> mGraphicBufferAlloc;
-
     EGLDisplay mDisplay;
     EGLContext mContext;
     EGLSurface mDummySurface;
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index c47b0c8..ec1e543 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -16,7 +16,6 @@
 
 #define ATRACE_TAG ATRACE_TAG_ALWAYS
 
-#include <gui/GraphicBufferAlloc.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceControl.h>
 #include <gui/GLConsumer.h>
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
new file mode 100644
index 0000000..33db6db
--- /dev/null
+++ b/cmds/installd/Android.bp
@@ -0,0 +1,75 @@
+cc_defaults {
+    name: "installd_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: [
+        "CacheItem.cpp",
+        "CacheTracker.cpp",
+        "InstalldNativeService.cpp",
+        "dexopt.cpp",
+        "globals.cpp",
+        "utils.cpp",
+        "binder/android/os/IInstalld.aidl",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "liblogwrap",
+        "libselinux",
+        "libutils",
+    ],
+
+    clang: true,
+}
+
+//
+// Static library used in testing and executable
+//
+
+cc_library_static {
+    name: "libinstalld",
+    defaults: ["installd_defaults"],
+
+    export_include_dirs: ["."],
+    aidl: {
+        export_aidl_headers: true,
+    },
+}
+
+//
+// Executable
+//
+
+cc_binary {
+    name: "installd",
+    defaults: ["installd_defaults"],
+    srcs: ["installd.cpp"],
+
+    static_libs: ["libdiskusage"],
+
+    init_rc: ["installd.rc"],
+}
+
+// OTA chroot tool
+
+cc_binary {
+    name: "otapreopt_chroot",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    clang: true,
+
+    srcs: ["otapreopt_chroot.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 86df596..1d21b3c 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -1,56 +1,12 @@
 LOCAL_PATH := $(call my-dir)
 
-common_src_files := commands.cpp globals.cpp utils.cpp
-common_cflags := -Wall -Werror
-
-#
-# Static library used in testing and executable
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libinstalld
-LOCAL_MODULE_TAGS := eng tests
-LOCAL_SRC_FILES := $(common_src_files)
-LOCAL_CFLAGS := $(common_cflags)
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblogwrap \
-    libselinux \
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
-#
-# Executable
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := installd
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
-LOCAL_SRC_FILES := installd.cpp $(common_src_files)
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libcutils \
-    liblog \
-    liblogwrap \
-    libselinux \
-
-LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_INIT_RC := installd.rc
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
-
 #
 # OTA Executable
 #
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := otapreopt
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CFLAGS := -Wall -Werror
 
 # Base & ASLR boundaries for boot image creation.
 ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
@@ -67,32 +23,17 @@
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
 
-LOCAL_SRC_FILES := otapreopt.cpp $(common_src_files)
+LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp
+LOCAL_HEADER_LIBRARIES := dex2oat_headers
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     libcutils \
     liblog \
     liblogwrap \
     libselinux \
+    libutils \
 
 LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
-
-# OTA chroot tool
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := otapreopt_chroot
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(common_cflags)
-
-LOCAL_SRC_FILES := otapreopt_chroot.cpp
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblog \
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
@@ -120,7 +61,3 @@
 LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot
 
 include $(BUILD_PREBUILT)
-
-# Tests.
-
-include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
new file mode 100644
index 0000000..515f915
--- /dev/null
+++ b/cmds/installd/CacheItem.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CacheItem.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CacheItem::CacheItem(FTSENT* p) {
+    level = p->fts_level;
+    directory = S_ISDIR(p->fts_statp->st_mode);
+    size = p->fts_statp->st_blocks * 512;
+    modified = p->fts_statp->st_mtime;
+
+    mParent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+    if (mParent) {
+        group = mParent->group;
+        tombstone = mParent->tombstone;
+        mName = p->fts_name;
+        mName.insert(0, "/");
+    } else {
+        group = false;
+        tombstone = false;
+        mName = p->fts_path;
+    }
+}
+
+CacheItem::~CacheItem() {
+}
+
+std::string CacheItem::toString() {
+    return StringPrintf("%s size=%" PRId64 " mod=%ld", buildPath().c_str(), size, modified);
+}
+
+std::string CacheItem::buildPath() {
+    std::string res = mName;
+    CacheItem* parent = mParent;
+    while (parent) {
+        res.insert(0, parent->mName);
+        parent = parent->mParent;
+    }
+    return res;
+}
+
+int CacheItem::purge() {
+    int res = 0;
+    auto path = buildPath();
+    if (directory) {
+        FTS *fts;
+        FTSENT *p;
+        char *argv[] = { (char*) path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+            PLOG(WARNING) << "Failed to fts_open " << path;
+            return -1;
+        }
+        while ((p = fts_read(fts)) != nullptr) {
+            switch (p->fts_info) {
+            case FTS_D:
+                if (p->fts_level == 0) {
+                    p->fts_number = tombstone;
+                } else {
+                    p->fts_number = p->fts_parent->fts_number
+                            | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+                }
+                break;
+            case FTS_F:
+                if (p->fts_parent->fts_number) {
+                    if (truncate(p->fts_path, 0) != 0) {
+                        PLOG(WARNING) << "Failed to truncate " << p->fts_path;
+                        res = -1;
+                    }
+                } else {
+                    if (unlink(p->fts_path) != 0) {
+                        PLOG(WARNING) << "Failed to unlink " << p->fts_path;
+                        res = -1;
+                    }
+                }
+                break;
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                if (unlink(p->fts_path) != 0) {
+                    PLOG(WARNING) << "Failed to unlink " << p->fts_path;
+                    res = -1;
+                }
+                break;
+            case FTS_DP:
+                if (rmdir(p->fts_path) != 0) {
+                    PLOG(WARNING) << "Failed to rmdir " << p->fts_path;
+                    res = -1;
+                }
+                break;
+            }
+        }
+    } else {
+        if (tombstone) {
+            if (truncate(path.c_str(), 0) != 0) {
+                PLOG(WARNING) << "Failed to truncate " << path;
+                res = -1;
+            }
+        } else {
+            if (unlink(path.c_str()) != 0) {
+                PLOG(WARNING) << "Failed to unlink " << path;
+                res = -1;
+            }
+        }
+    }
+    return res;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h
new file mode 100644
index 0000000..84b77aa
--- /dev/null
+++ b/cmds/installd/CacheItem.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_CACHE_ITEM_H
+#define ANDROID_INSTALLD_CACHE_ITEM_H
+
+#include <memory>
+#include <string>
+
+#include <fts.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace installd {
+
+/**
+ * Single cache item that can be purged to free up space. This may be an
+ * isolated file, or an entire directory tree that should be deleted as a
+ * group.
+ */
+class CacheItem {
+public:
+    CacheItem(FTSENT* p);
+    ~CacheItem();
+
+    std::string toString();
+    std::string buildPath();
+
+    int purge();
+
+    short level;
+    bool directory;
+    bool group;
+    bool tombstone;
+    int64_t size;
+    time_t modified;
+
+private:
+    CacheItem* mParent;
+    std::string mName;
+
+    DISALLOW_COPY_AND_ASSIGN(CacheItem);
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_CACHE_ITEM_H
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
new file mode 100644
index 0000000..e293948
--- /dev/null
+++ b/cmds/installd/CacheTracker.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include "CacheTracker.h"
+
+#include <fts.h>
+#include <sys/quota.h>
+#include <sys/xattr.h>
+#include <utils/Trace.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice) :
+        cacheUsed(0), cacheQuota(0), mUserId(userId), mAppId(appId), mQuotaDevice(quotaDevice),
+        mItemsLoaded(false) {
+}
+
+CacheTracker::~CacheTracker() {
+}
+
+std::string CacheTracker::toString() {
+    return StringPrintf("UID=%d used=%" PRId64 " quota=%" PRId64 " ratio=%d",
+            multiuser_get_uid(mUserId, mAppId), cacheUsed, cacheQuota, getCacheRatio());
+}
+
+void CacheTracker::addDataPath(const std::string& dataPath) {
+    mDataPaths.push_back(dataPath);
+}
+
+void CacheTracker::loadStats() {
+    ATRACE_BEGIN("loadStats quota");
+    cacheUsed = 0;
+    if (loadQuotaStats()) {
+        return;
+    }
+    ATRACE_END();
+
+    ATRACE_BEGIN("loadStats tree");
+    cacheUsed = 0;
+    for (auto path : mDataPaths) {
+        auto cachePath = read_path_inode(path, "cache", kXattrInodeCache);
+        auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+        calculate_tree_size(cachePath, &cacheUsed);
+        calculate_tree_size(codeCachePath, &cacheUsed);
+    }
+    ATRACE_END();
+}
+
+bool CacheTracker::loadQuotaStats() {
+    int cacheGid = multiuser_get_cache_gid(mUserId, mAppId);
+    int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId);
+    if (!mQuotaDevice.empty() && cacheGid != -1 && extCacheGid != -1) {
+        struct dqblk dq;
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), cacheGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid;
+            }
+            return false;
+        } else {
+            cacheUsed += dq.dqb_curspace;
+        }
+
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), extCacheGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid;
+            }
+            return false;
+        } else {
+            cacheUsed += dq.dqb_curspace;
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void CacheTracker::loadItemsFrom(const std::string& path) {
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+        PLOG(WARNING) << "Failed to fts_open " << path;
+        return;
+    }
+    while ((p = fts_read(fts)) != nullptr) {
+        if (p->fts_level == 0) continue;
+
+        // Create tracking nodes for everything we encounter
+        switch (p->fts_info) {
+        case FTS_D:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE: {
+            auto item = std::shared_ptr<CacheItem>(new CacheItem(p));
+            p->fts_pointer = static_cast<void*>(item.get());
+            items.push_back(item);
+        }
+        }
+
+        switch (p->fts_info) {
+        case FTS_D: {
+            auto item = static_cast<CacheItem*>(p->fts_pointer);
+            item->group |= (getxattr(p->fts_path, kXattrCacheGroup, nullptr, 0) >= 0);
+            item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+
+            // When group, immediately collect all files under tree
+            if (item->group) {
+                while ((p = fts_read(fts)) != nullptr) {
+                    if (p->fts_info == FTS_DP && p->fts_level == item->level) break;
+                    switch (p->fts_info) {
+                    case FTS_D:
+                    case FTS_DEFAULT:
+                    case FTS_F:
+                    case FTS_SL:
+                    case FTS_SLNONE:
+                        item->size += p->fts_statp->st_blocks * 512;
+                        item->modified = std::max(item->modified, p->fts_statp->st_mtime);
+                    }
+                }
+            }
+        }
+        }
+
+        // Bubble up modified time to parent
+        switch (p->fts_info) {
+        case FTS_DP:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE: {
+            auto item = static_cast<CacheItem*>(p->fts_pointer);
+            auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+            if (parent) {
+                parent->modified = std::max(parent->modified, item->modified);
+            }
+        }
+        }
+    }
+    fts_close(fts);
+}
+
+void CacheTracker::loadItems() {
+    items.clear();
+
+    ATRACE_BEGIN("loadItems");
+    for (auto path : mDataPaths) {
+        loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache));
+        loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache));
+    }
+    ATRACE_END();
+
+    ATRACE_BEGIN("sortItems");
+    auto cmp = [](std::shared_ptr<CacheItem> left, std::shared_ptr<CacheItem> right) {
+        // TODO: sort dotfiles last
+        // TODO: sort code_cache last
+        if (left->modified != right->modified) {
+            return (left->modified > right->modified);
+        }
+        if (left->level != right->level) {
+            return (left->level < right->level);
+        }
+        return left->directory;
+    };
+    std::stable_sort(items.begin(), items.end(), cmp);
+    ATRACE_END();
+}
+
+void CacheTracker::ensureItems() {
+    if (mItemsLoaded) {
+        return;
+    } else {
+        loadItems();
+        mItemsLoaded = true;
+    }
+}
+
+int CacheTracker::getCacheRatio() {
+    if (cacheQuota == 0) {
+        return 0;
+    } else {
+        return (cacheUsed * 10000) / cacheQuota;
+    }
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/CacheTracker.h b/cmds/installd/CacheTracker.h
new file mode 100644
index 0000000..44359b4
--- /dev/null
+++ b/cmds/installd/CacheTracker.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_CACHE_TRACKER_H
+#define ANDROID_INSTALLD_CACHE_TRACKER_H
+
+#include <memory>
+#include <string>
+#include <queue>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+#include <cutils/multiuser.h>
+
+#include "CacheItem.h"
+
+namespace android {
+namespace installd {
+
+/**
+ * Cache tracker for a single UID. Each tracker is used in two modes: first
+ * for loading lightweight "stats", and then by loading detailed "items"
+ * which can then be purged to free up space.
+ */
+class CacheTracker {
+public:
+    CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice);
+    ~CacheTracker();
+
+    std::string toString();
+
+    void addDataPath(const std::string& dataPath);
+
+    void loadStats();
+    void loadItems();
+
+    void ensureItems();
+
+    int getCacheRatio();
+
+    int64_t cacheUsed;
+    int64_t cacheQuota;
+
+    std::vector<std::shared_ptr<CacheItem>> items;
+
+private:
+    userid_t mUserId;
+    appid_t mAppId;
+    std::string mQuotaDevice;
+    bool mItemsLoaded;
+
+    std::vector<std::string> mDataPaths;
+
+    bool loadQuotaStats();
+    void loadItemsFrom(const std::string& path);
+
+    DISALLOW_COPY_AND_ASSIGN(CacheTracker);
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_CACHE_TRACKER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
new file mode 100644
index 0000000..60c89a9
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -0,0 +1,2357 @@
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "InstalldNativeService.h"
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include <errno.h>
+#include <inttypes.h>
+#include <fstream>
+#include <fts.h>
+#include <regex>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#include <sys/quota.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>               // TODO: Move everything to base/logging.
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <system/thread_defs.h>
+#include <utils/Trace.h>
+
+#include "dexopt.h"
+#include "globals.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+#include "CacheTracker.h"
+#include "MatchExtensionGen.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "installd"
+#endif
+
+using android::base::StringPrintf;
+using std::endl;
+
+namespace android {
+namespace installd {
+
+static constexpr const char* kCpPath = "/system/bin/cp";
+static constexpr const char* kXattrDefault = "user.default";
+
+static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
+
+static constexpr const char* PKG_LIB_POSTFIX = "/lib";
+static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
+static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
+
+static constexpr const char *kIdMapPath = "/system/bin/idmap";
+static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
+static constexpr const char* IDMAP_SUFFIX = "@idmap";
+
+// NOTE: keep in sync with Installer
+static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+static constexpr int FLAG_USE_QUOTA = 1 << 12;
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15;
+static constexpr int FLAG_FORCE = 1 << 16;
+
+namespace {
+
+constexpr const char* kDump = "android.permission.DUMP";
+
+static binder::Status ok() {
+    return binder::Status::ok();
+}
+
+static binder::Status exception(uint32_t code, const std::string& msg) {
+    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+}
+
+static binder::Status error() {
+    return binder::Status::fromServiceSpecificError(errno);
+}
+
+static binder::Status error(const std::string& msg) {
+    PLOG(ERROR) << msg;
+    return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
+}
+
+static binder::Status error(uint32_t code, const std::string& msg) {
+    LOG(ERROR) << msg << " (" << code << ")";
+    return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
+}
+
+binder::Status checkPermission(const char* permission) {
+    pid_t pid;
+    uid_t uid;
+
+    if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
+            reinterpret_cast<int32_t*>(&uid))) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
+    }
+}
+
+binder::Status checkUid(uid_t expectedUid) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid == expectedUid || uid == AID_ROOT) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
+    }
+}
+
+binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+    if (!uuid || is_valid_filename(*uuid)) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("UUID %s is malformed", uuid->c_str()));
+    }
+}
+
+binder::Status checkArgumentPackageName(const std::string& packageName) {
+    if (is_valid_package_name(packageName.c_str())) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("Package name %s is malformed", packageName.c_str()));
+    }
+}
+
+#define ENFORCE_UID(uid) {                                  \
+    binder::Status status = checkUid((uid));                \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_UUID(uuid) {                         \
+    binder::Status status = checkArgumentUuid((uuid));      \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_PACKAGE_NAME(packageName) {          \
+    binder::Status status =                                 \
+            checkArgumentPackageName((packageName));        \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+}  // namespace
+
+status_t InstalldNativeService::start() {
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    status_t ret = BinderService<InstalldNativeService>::publish();
+    if (ret != android::OK) {
+        return ret;
+    }
+    sp<ProcessState> ps(ProcessState::self());
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    return android::OK;
+}
+
+status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
+    auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd));
+    const binder::Status dump_permission = checkPermission(kDump);
+    if (!dump_permission.isOk()) {
+        out << dump_permission.toString8() << endl;
+        return PERMISSION_DENIED;
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    out << "installd is happy!" << endl;
+
+    {
+        std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+        out << endl << "Storage mounts:" << endl;
+        for (const auto& n : mStorageMounts) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
+
+        out << endl << "Quota reverse mounts:" << endl;
+        for (const auto& n : mQuotaReverseMounts) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
+    }
+
+    {
+        std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
+        out << endl << "Per-UID cache quotas:" << endl;
+        for (const auto& n : mCacheQuotas) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
+    }
+
+    out << endl;
+    out.flush();
+
+    return NO_ERROR;
+}
+
+/**
+ * Perform restorecon of the given path, but only perform recursive restorecon
+ * if the label of that top-level file actually changed.  This can save us
+ * significant time by avoiding no-op traversals of large filesystem trees.
+ */
+static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
+        bool existing) {
+    int res = 0;
+    char* before = nullptr;
+    char* after = nullptr;
+
+    // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
+    // libselinux. Not needed here.
+
+    if (lgetfilecon(path.c_str(), &before) < 0) {
+        PLOG(ERROR) << "Failed before getfilecon for " << path;
+        goto fail;
+    }
+    if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
+        PLOG(ERROR) << "Failed top-level restorecon for " << path;
+        goto fail;
+    }
+    if (lgetfilecon(path.c_str(), &after) < 0) {
+        PLOG(ERROR) << "Failed after getfilecon for " << path;
+        goto fail;
+    }
+
+    // If the initial top-level restorecon above changed the label, then go
+    // back and restorecon everything recursively
+    if (strcmp(before, after)) {
+        if (existing) {
+            LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
+                    << path << "; running recursive restorecon";
+        }
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+                SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+            PLOG(ERROR) << "Failed recursive restorecon for " << path;
+            goto fail;
+        }
+    }
+
+    goto done;
+fail:
+    res = -1;
+done:
+    free(before);
+    free(after);
+    return res;
+}
+
+static int restorecon_app_data_lazy(const std::string& parent, const char* name,
+        const std::string& seInfo, uid_t uid, bool existing) {
+    return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
+            existing);
+}
+
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
+    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << path;
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Ensure that we have a hard-limit quota to protect against abusive apps;
+ * they should never use more than 90% of blocks or 50% of inodes.
+ */
+static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device,
+        uid_t uid) {
+    if (device.empty()) return 0;
+
+    struct dqblk dq;
+    if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+            reinterpret_cast<char*>(&dq)) != 0) {
+        PLOG(WARNING) << "Failed to find quota for " << uid;
+        return -1;
+    }
+
+    if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) {
+        auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+        struct statvfs stat;
+        if (statvfs(path.c_str(), &stat) != 0) {
+            PLOG(WARNING) << "Failed to statvfs " << path;
+            return -1;
+        }
+
+        dq.dqb_valid = QIF_LIMITS;
+        dq.dqb_bhardlimit = (((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) {
+            PLOG(WARNING) << "Failed to set hard quota for " << uid;
+            return -1;
+        } else {
+            LOG(DEBUG) << "Applied hard quotas for " << uid;
+            return 0;
+        }
+    } else {
+        // Hard quota already set; assume it's reasonable
+        return 0;
+    }
+}
+
+binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+        const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    // Assume invalid inode unless filled in below
+    if (_aidl_return != nullptr) *_aidl_return = -1;
+
+    int32_t uid = multiuser_get_uid(userId, appId);
+    int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+    mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+
+    // If UID doesn't have a specific cache GID, use UID value
+    if (cacheGid == -1) {
+        cacheGid = uid;
+    }
+
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+        bool existing = (access(path.c_str(), F_OK) == 0);
+
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+            return error("Failed to prepare " + path);
+        }
+
+        // Consider restorecon over contents if label changed
+        if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+            return error("Failed to restorecon " + path);
+        }
+
+        // Remember inode numbers of cache directories so that we can clear
+        // contents while CE storage is locked
+        if (write_path_inode(path, "cache", kXattrInodeCache) ||
+                write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
+            return error("Failed to write_path_inode for " + path);
+        }
+
+        // And return the CE inode of the top-level data directory so we can
+        // clear contents while CE storage is locked
+        if ((_aidl_return != nullptr)
+                && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
+            return error("Failed to get_path_inode for " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+        bool existing = (access(path.c_str(), F_OK) == 0);
+
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+            return error("Failed to prepare " + path);
+        }
+
+        // Consider restorecon over contents if label changed
+        if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+            return error("Failed to restorecon " + path);
+        }
+
+        if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) {
+            return error("Failed to set hard quota " + path);
+        }
+
+        if (property_get_bool("dalvik.vm.usejitprofiles", false)) {
+            const std::string profile_dir =
+                    create_primary_current_profile_package_dir_path(userId, pkgname);
+            // read-write-execute only for the app user.
+            if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) {
+                return error("Failed to prepare " + profile_dir);
+            }
+            const std::string profile_file = create_current_profile_path(userId, pkgname,
+                    /*is_secondary_dex*/false);
+            // read-write only for the app user.
+            if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
+                return error("Failed to prepare " + profile_file);
+            }
+            const std::string ref_profile_path =
+                    create_primary_reference_profile_package_dir_path(pkgname);
+            // dex2oat/profman runs under the shared app gid and it needs to read/write reference
+            // profiles.
+            int shared_app_gid = multiuser_get_shared_gid(0, appId);
+            if ((shared_app_gid != -1) && fs_prepare_dir_strict(
+                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
+                return error("Failed to prepare " + ref_profile_path);
+            }
+        }
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    // This method only exists to upgrade system apps that have requested
+    // forceDeviceEncrypted, so their default storage always lives in a
+    // consistent location.  This only works on non-FBE devices, since we
+    // never want to risk exposing data on a device with real CE/DE storage.
+
+    auto ce_path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+    auto de_path = create_data_user_de_package_path(uuid_, userId, pkgname);
+
+    // If neither directory is marked as default, assume CE is default
+    if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
+            && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
+        if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
+            return error("Failed to mark default storage " + ce_path);
+        }
+    }
+
+    // Migrate default data location if needed
+    auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
+    auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
+
+    if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
+        LOG(WARNING) << "Requested default storage " << target
+                << " is not active; migrating from " << source;
+        if (delete_dir_contents_and_dir(target) != 0) {
+            return error("Failed to delete " + target);
+        }
+        if (rename(source.c_str(), target.c_str()) != 0) {
+            return error("Failed to rename " + source + " to " + target);
+        }
+    }
+
+    return ok();
+}
+
+
+binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    binder::Status res = ok();
+    if (!clear_primary_reference_profile(packageName)) {
+        res = error("Failed to clear reference profile for " + packageName);
+    }
+    if (!clear_primary_current_profiles(packageName)) {
+        res = error("Failed to clear current profiles for " + packageName);
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            path = read_path_inode(path, "cache", kXattrInodeCache);
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+        }
+        if (access(path.c_str(), F_OK) == 0) {
+            if (delete_dir_contents(path) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        std::string suffix = "";
+        bool only_cache = false;
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            suffix = CACHE_DIR_POSTFIX;
+            only_cache = true;
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            suffix = CODE_CACHE_DIR_POSTFIX;
+            only_cache = true;
+        }
+
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
+        if (access(path.c_str(), F_OK) == 0) {
+            if (delete_dir_contents(path) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        }
+        if (!only_cache) {
+            if (!clear_primary_current_profile(packageName, userId)) {
+                res = error("Failed to clear current profile for " + packageName);
+            }
+        }
+    }
+    return res;
+}
+
+static int destroy_app_reference_profile(const std::string& pkgname) {
+    return delete_dir_contents_and_dir(
+        create_primary_reference_profile_package_dir_path(pkgname),
+        /*ignore_if_missing*/ true);
+}
+
+static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) {
+    return delete_dir_contents_and_dir(
+        create_primary_current_profile_package_dir_path(userid, pkgname),
+        /*ignore_if_missing*/ true);
+}
+
+binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    binder::Status res = ok();
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        if (destroy_app_current_profiles(packageName, user) != 0) {
+            res = error("Failed to destroy current profiles for " + packageName);
+        }
+    }
+    if (destroy_app_reference_profile(packageName) != 0) {
+        res = error("Failed to destroy reference profile for " + packageName);
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+        if (delete_dir_contents_and_dir(path) != 0) {
+            res = error("Failed to delete " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+        if (delete_dir_contents_and_dir(path) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        destroy_app_current_profiles(packageName, userId);
+        // TODO(calin): If the package is still installed by other users it's probably
+        // beneficial to keep the reference profile around.
+        // Verify if it's ok to do that.
+        destroy_app_reference_profile(packageName);
+    }
+    return res;
+}
+
+static gid_t get_cache_gid(uid_t uid) {
+    int32_t gid = multiuser_get_cache_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
+    return (gid != -1) ? gid : uid;
+}
+
+binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid,
+        int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    for (auto user : get_known_users(uuid_)) {
+        ATRACE_BEGIN("fixup user");
+        FTS* fts;
+        FTSENT* p;
+        auto ce_path = create_data_user_ce_path(uuid_, user);
+        auto de_path = create_data_user_de_path(uuid_, user);
+        char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+            return error("Failed to fts_open");
+        }
+        while ((p = fts_read(fts)) != nullptr) {
+            if (p->fts_info == FTS_D && p->fts_level == 1) {
+                // Track down inodes of cache directories
+                uint64_t raw = 0;
+                ino_t inode_cache = 0;
+                ino_t inode_code_cache = 0;
+                if (getxattr(p->fts_path, kXattrInodeCache, &raw, sizeof(raw)) == sizeof(raw)) {
+                    inode_cache = raw;
+                }
+                if (getxattr(p->fts_path, kXattrInodeCodeCache, &raw, sizeof(raw)) == sizeof(raw)) {
+                    inode_code_cache = raw;
+                }
+
+                // Figure out expected GID of each child
+                FTSENT* child = fts_children(fts, 0);
+                while (child != nullptr) {
+                    if ((child->fts_statp->st_ino == inode_cache)
+                            || (child->fts_statp->st_ino == inode_code_cache)
+                            || !strcmp(child->fts_name, "cache")
+                            || !strcmp(child->fts_name, "code_cache")) {
+                        child->fts_number = get_cache_gid(p->fts_statp->st_uid);
+                    } else {
+                        child->fts_number = p->fts_statp->st_uid;
+                    }
+                    child = child->fts_link;
+                }
+            } else if (p->fts_level >= 2) {
+                if (p->fts_level > 2) {
+                    // Inherit GID from parent once we're deeper into tree
+                    p->fts_number = p->fts_parent->fts_number;
+                }
+
+                uid_t uid = p->fts_parent->fts_statp->st_uid;
+                gid_t cache_gid = get_cache_gid(uid);
+                gid_t expected = p->fts_number;
+                gid_t actual = p->fts_statp->st_gid;
+                if (actual == expected) {
+#if FIXUP_DEBUG
+                    LOG(DEBUG) << "Ignoring " << p->fts_path << " with expected GID " << expected;
+#endif
+                    if (!(flags & FLAG_FORCE)) {
+                        fts_set(fts, p, FTS_SKIP);
+                    }
+                } else if ((actual == uid) || (actual == cache_gid)) {
+                    // Only consider fixing up when current GID belongs to app
+                    if (p->fts_info != FTS_D) {
+                        LOG(INFO) << "Fixing " << p->fts_path << " with unexpected GID " << actual
+                                << " instead of " << expected;
+                    }
+                    switch (p->fts_info) {
+                    case FTS_DP:
+                        // If we're moving towards cache GID, we need to set S_ISGID
+                        if (expected == cache_gid) {
+                            if (chmod(p->fts_path, 02771) != 0) {
+                                PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+                            }
+                        }
+                        // Intentional fall through to also set GID
+                    case FTS_F:
+                        if (chown(p->fts_path, -1, expected) != 0) {
+                            PLOG(WARNING) << "Failed to chown " << p->fts_path;
+                        }
+                        break;
+                    case FTS_SL:
+                    case FTS_SLNONE:
+                        if (lchown(p->fts_path, -1, expected) != 0) {
+                            PLOG(WARNING) << "Failed to chown " << p->fts_path;
+                        }
+                        break;
+                    }
+                } else {
+                    // Ignore all other GID transitions, since they're kinda shady
+                    LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected GID " << actual
+                            << " instead of " << expected;
+                }
+            }
+        }
+        fts_close(fts);
+        ATRACE_END();
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+        const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+        const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+        int32_t targetSdkVersion) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(fromUuid);
+    CHECK_ARGUMENT_UUID(toUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
+    const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+    const char* data_app_name = dataAppName.c_str();
+
+    binder::Status res = ok();
+    std::vector<userid_t> users = get_known_users(from_uuid);
+
+    // Copy app
+    {
+        auto from = create_data_app_package_path(from_uuid, data_app_name);
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
+        auto to_parent = create_data_app_path(to_uuid);
+
+        char *argv[] = {
+            (char*) kCpPath,
+            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+            (char*) "-p", /* preserve timestamps, ownership, and permissions */
+            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+            (char*) "-P", /* Do not follow symlinks [default] */
+            (char*) "-d", /* don't dereference symlinks */
+            (char*) from.c_str(),
+            (char*) to_parent.c_str()
+        };
+
+        LOG(DEBUG) << "Copying " << from << " to " << to;
+        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from + " to " + to);
+            goto fail;
+        }
+
+        if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            res = error("Failed to restorecon " + to);
+            goto fail;
+        }
+    }
+
+    // Copy private data for all known users
+    for (auto user : users) {
+
+        // Data source may not exist for all users; that's okay
+        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+        if (access(from_ce.c_str(), F_OK) != 0) {
+            LOG(INFO) << "Missing source " << from_ce;
+            continue;
+        }
+
+        if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
+                seInfo, targetSdkVersion, nullptr).isOk()) {
+            res = error("Failed to create package target");
+            goto fail;
+        }
+
+        char *argv[] = {
+            (char*) kCpPath,
+            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+            (char*) "-p", /* preserve timestamps, ownership, and permissions */
+            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+            (char*) "-P", /* Do not follow symlinks [default] */
+            (char*) "-d", /* don't dereference symlinks */
+            nullptr,
+            nullptr
+        };
+
+        {
+            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_de_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
+
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                res = error(rc, "Failed copying " + from + " to " + to);
+                goto fail;
+            }
+        }
+        {
+            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_ce_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
+
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                res = error(rc, "Failed copying " + from + " to " + to);
+                goto fail;
+            }
+        }
+
+        if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+                appId, seInfo).isOk()) {
+            res = error("Failed to restorecon");
+            goto fail;
+        }
+    }
+
+    // We let the framework scan the new location and persist that before
+    // deleting the data in the old location; this ordering ensures that
+    // we can recover from things like battery pulls.
+    return ok();
+
+fail:
+    // Nuke everything we might have already copied
+    {
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
+        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+            LOG(WARNING) << "Failed to rollback " << to;
+        }
+    }
+    for (auto user : users) {
+        {
+            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+        {
+            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    if (flags & FLAG_STORAGE_DE) {
+        if (uuid_ == nullptr) {
+            if (ensure_config_user_dirs(userId) != 0) {
+                return error(StringPrintf("Failed to ensure dirs for %d", userId));
+            }
+        }
+    }
+
+    // Data under /data/media doesn't have an app, but we still want
+    // to limit it to prevent abuse.
+    if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid),
+            multiuser_get_uid(userId, AID_MEDIA_RW))) {
+        return error("Failed to set hard quota for media_rw");
+    }
+
+    return ok();
+}
+
+binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_path(uuid_, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        if (uuid_ == nullptr) {
+            path = create_data_misc_legacy_path(userId);
+            if (delete_dir_contents_and_dir(path, true) != 0) {
+                res = error("Failed to delete " + path);
+            }
+            path = create_primary_cur_profile_dir_path(userId);
+            if (delete_dir_contents_and_dir(path, true) != 0) {
+                res = error("Failed to delete " + path);
+            }
+        }
+    }
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_path(uuid_, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        path = findDataMediaPath(uuid, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+        int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    auto data_path = create_data_path(uuid_);
+    auto device = findQuotaDeviceForUuid(uuid);
+    auto noop = (flags & FLAG_FREE_CACHE_NOOP);
+
+    int64_t free = data_disk_free(data_path);
+    if (free < 0) {
+        return error("Failed to determine free space for " + data_path);
+    }
+
+    int64_t cleared = 0;
+    int64_t needed = targetFreeBytes - free;
+    LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
+            << targetFreeBytes << "; needed " << needed;
+
+    if (free >= targetFreeBytes) {
+        return ok();
+    }
+
+    if (flags & FLAG_FREE_CACHE_V2) {
+        // This new cache strategy fairly removes files from UIDs by deleting
+        // files from the UIDs which are most over their allocated quota
+
+        // 1. Create trackers for every known UID
+        ATRACE_BEGIN("create");
+        std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers;
+        for (auto user : get_known_users(uuid_)) {
+            FTS *fts;
+            FTSENT *p;
+            auto ce_path = create_data_user_ce_path(uuid_, user);
+            auto de_path = create_data_user_de_path(uuid_, user);
+            auto media_path = findDataMediaPath(uuid, user) + "/Android/data/";
+            char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(),
+                    (char*) media_path.c_str(), nullptr };
+            if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+                return error("Failed to fts_open");
+            }
+            while ((p = fts_read(fts)) != NULL) {
+                if (p->fts_info == FTS_D && p->fts_level == 1) {
+                    uid_t uid = p->fts_statp->st_uid;
+                    if (multiuser_get_app_id(uid) == AID_MEDIA_RW) {
+                        uid = (multiuser_get_app_id(p->fts_statp->st_gid) - AID_EXT_GID_START)
+                                + AID_APP_START;
+                    }
+                    auto search = trackers.find(uid);
+                    if (search != trackers.end()) {
+                        search->second->addDataPath(p->fts_path);
+                    } else {
+                        auto tracker = std::shared_ptr<CacheTracker>(new CacheTracker(
+                                multiuser_get_user_id(uid), multiuser_get_app_id(uid), device));
+                        tracker->addDataPath(p->fts_path);
+                        {
+                            std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
+                            tracker->cacheQuota = mCacheQuotas[uid];
+                        }
+                        if (tracker->cacheQuota == 0) {
+#if MEASURE_DEBUG
+                            LOG(WARNING) << "UID " << uid << " has no cache quota; assuming 64MB";
+#endif
+                            tracker->cacheQuota = 67108864;
+                        }
+                        trackers[uid] = tracker;
+                    }
+                    fts_set(fts, p, FTS_SKIP);
+                }
+            }
+            fts_close(fts);
+        }
+        ATRACE_END();
+
+        // 2. Populate tracker stats and insert into priority queue
+        ATRACE_BEGIN("populate");
+        int64_t cacheTotal = 0;
+        auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) {
+            return (left->getCacheRatio() < right->getCacheRatio());
+        };
+        std::priority_queue<std::shared_ptr<CacheTracker>,
+                std::vector<std::shared_ptr<CacheTracker>>, decltype(cmp)> queue(cmp);
+        for (const auto& it : trackers) {
+            it.second->loadStats();
+            queue.push(it.second);
+            cacheTotal += it.second->cacheUsed;
+        }
+        ATRACE_END();
+
+        // 3. Bounce across the queue, freeing items from whichever tracker is
+        // the most over their assigned quota
+        ATRACE_BEGIN("bounce");
+        std::shared_ptr<CacheTracker> active;
+        while (active || !queue.empty()) {
+            // Only look at apps under quota when explicitly requested
+            if (active && (active->getCacheRatio() < 10000)
+                    && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) {
+                LOG(DEBUG) << "Active ratio " << active->getCacheRatio()
+                        << " isn't over quota, and defy not requested";
+                break;
+            }
+
+            // Only keep clearing when we haven't pushed into reserved area
+            if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
+                LOG(DEBUG) << "Refusing to clear cached data in reserved space";
+                break;
+            }
+
+            // Find the best tracker to work with; this might involve swapping
+            // if the active tracker is no longer the most over quota
+            bool nextBetter = active && !queue.empty()
+                    && active->getCacheRatio() < queue.top()->getCacheRatio();
+            if (!active || nextBetter) {
+                if (active) {
+                    // Current tracker still has items, so we'll consider it
+                    // again later once it bubbles up to surface
+                    queue.push(active);
+                }
+                active = queue.top(); queue.pop();
+                active->ensureItems();
+                continue;
+            }
+
+            // If no items remain, go find another tracker
+            if (active->items.empty()) {
+                active = nullptr;
+                continue;
+            } else {
+                auto item = active->items.back();
+                active->items.pop_back();
+
+                LOG(DEBUG) << "Purging " << item->toString() << " from " << active->toString();
+                if (!noop) {
+                    item->purge();
+                }
+                active->cacheUsed -= item->size;
+                needed -= item->size;
+                cleared += item->size;
+            }
+
+            // Verify that we're actually done before bailing, since sneaky
+            // apps might be using hardlinks
+            if (needed <= 0) {
+                free = data_disk_free(data_path);
+                needed = targetFreeBytes - free;
+                if (needed <= 0) {
+                    break;
+                } else {
+                    LOG(WARNING) << "Expected to be done but still need " << needed;
+                }
+            }
+        }
+        ATRACE_END();
+
+    } else {
+        return error("Legacy cache logic no longer supported");
+    }
+
+    free = data_disk_free(data_path);
+    if (free >= targetFreeBytes) {
+        return ok();
+    } else {
+        return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
+                targetFreeBytes, data_path.c_str(), free));
+    }
+}
+
+binder::Status InstalldNativeService::rmdex(const std::string& codePath,
+        const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    char dex_path[PKG_PATH_MAX];
+
+    const char* path = codePath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+
+    if (validate_apk_path(path) && validate_system_app_path(path)) {
+        return error("Invalid path " + codePath);
+    }
+
+    if (!create_cache_path(dex_path, path, instruction_set)) {
+        return error("Failed to create cache path for " + codePath);
+    }
+
+    ALOGV("unlink %s\n", dex_path);
+    if (unlink(dex_path) < 0) {
+        // It's ok if we don't have a dalvik cache path. Report error only when the path exists
+        // but could not be unlinked.
+        if (errno != ENOENT) {
+            return error(StringPrintf("Failed to unlink %s", dex_path));
+        }
+    }
+    return ok();
+}
+
+struct stats {
+    int64_t codeSize;
+    int64_t dataSize;
+    int64_t cacheSize;
+};
+
+#if MEASURE_DEBUG
+static std::string toString(std::vector<int64_t> values) {
+    std::stringstream res;
+    res << "[";
+    for (size_t i = 0; i < values.size(); i++) {
+        res << values[i];
+        if (i < values.size() - 1) {
+            res << ",";
+        }
+    }
+    res << "]";
+    return res.str();
+}
+#endif
+
+static void collectQuotaStats(const std::string& device, int32_t userId,
+        int32_t appId, struct stats* stats, struct stats* extStats) {
+    if (device.empty()) return;
+
+    struct dqblk dq;
+
+    if (stats != nullptr) {
+        uid_t uid = multiuser_get_uid(userId, appId);
+        if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+            }
+        } else {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+            stats->dataSize += dq.dqb_curspace;
+        }
+
+        int cacheGid = multiuser_get_cache_gid(userId, appId);
+        if (cacheGid != -1) {
+            if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid,
+                    reinterpret_cast<char*>(&dq)) != 0) {
+                if (errno != ESRCH) {
+                    PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid;
+                }
+            } else {
+#if MEASURE_DEBUG
+                LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace;
+#endif
+                stats->cacheSize += dq.dqb_curspace;
+            }
+        }
+
+        int sharedGid = multiuser_get_shared_gid(0, appId);
+        if (sharedGid != -1) {
+            if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid,
+                    reinterpret_cast<char*>(&dq)) != 0) {
+                if (errno != ESRCH) {
+                    PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid;
+                }
+            } else {
+#if MEASURE_DEBUG
+                LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace;
+#endif
+                stats->codeSize += dq.dqb_curspace;
+            }
+        }
+    }
+
+    if (extStats != nullptr) {
+        int extGid = multiuser_get_ext_gid(userId, appId);
+        if (extGid != -1) {
+            if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extGid,
+                    reinterpret_cast<char*>(&dq)) != 0) {
+                if (errno != ESRCH) {
+                    PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extGid;
+                }
+            } else {
+#if MEASURE_DEBUG
+                LOG(DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace;
+#endif
+                extStats->dataSize += dq.dqb_curspace;
+            }
+        }
+
+        int extCacheGid = multiuser_get_ext_cache_gid(userId, appId);
+        if (extCacheGid != -1) {
+            if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extCacheGid,
+                    reinterpret_cast<char*>(&dq)) != 0) {
+                if (errno != ESRCH) {
+                    PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extCacheGid;
+                }
+            } else {
+#if MEASURE_DEBUG
+                LOG(DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace;
+#endif
+                extStats->dataSize += dq.dqb_curspace;
+                extStats->cacheSize += dq.dqb_curspace;
+            }
+        }
+    }
+}
+
+static void collectManualStats(const std::string& path, struct stats* stats) {
+    DIR *d;
+    int dfd;
+    struct dirent *de;
+    struct stat s;
+
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        const char *name = de->d_name;
+
+        int64_t size = 0;
+        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+            size = s.st_blocks * 512;
+        }
+
+        if (de->d_type == DT_DIR) {
+            if (!strcmp(name, ".")) {
+                // Don't recurse, but still count node size
+            } else if (!strcmp(name, "..")) {
+                // Don't recurse or count node size
+                continue;
+            } else {
+                // Measure all children nodes
+                size = 0;
+                calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size);
+            }
+
+            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
+                stats->cacheSize += size;
+            }
+        }
+
+        // Legacy symlink isn't owned by app
+        if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
+            continue;
+        }
+
+        // Everything found inside is considered data
+        stats->dataSize += size;
+    }
+    closedir(d);
+}
+
+static void collectManualStatsForUser(const std::string& path, struct stats* stats,
+        bool exclude_apps = false) {
+    DIR *d;
+    int dfd;
+    struct dirent *de;
+    struct stat s;
+
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        if (de->d_type == DT_DIR) {
+            const char *name = de->d_name;
+            if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) != 0) {
+                continue;
+            }
+            int32_t user_uid = multiuser_get_app_id(s.st_uid);
+            if (!strcmp(name, ".") || !strcmp(name, "..")) {
+                continue;
+            } else if (exclude_apps && (user_uid >= AID_APP_START && user_uid <= AID_APP_END)) {
+                continue;
+            } else {
+                collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats);
+            }
+        }
+    }
+    closedir(d);
+}
+
+static void collectManualExternalStatsForUser(const std::string& path, struct stats* stats) {
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+        PLOG(ERROR) << "Failed to fts_open " << path;
+        return;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        p->fts_number = p->fts_parent->fts_number;
+        switch (p->fts_info) {
+        case FTS_D:
+            if (p->fts_level == 4
+                    && !strcmp(p->fts_name, "cache")
+                    && !strcmp(p->fts_parent->fts_parent->fts_name, "data")
+                    && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
+                p->fts_number = 1;
+            }
+            // Fall through to count the directory
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE:
+            int64_t size = (p->fts_statp->st_blocks * 512);
+            if (p->fts_number == 1) {
+                stats->cacheSize += size;
+            }
+            stats->dataSize += size;
+            break;
+        }
+    }
+    fts_close(fts);
+}
+
+binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+        const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+        int32_t appId, const std::vector<int64_t>& ceDataInodes,
+        const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    for (auto packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId << " app " << appId;
+#endif
+
+    // Here's a summary of the common storage locations across the platform,
+    // and how they're each tagged:
+    //
+    // /data/app/com.example                           UID system
+    // /data/app/com.example/oat                       UID system
+    // /data/user/0/com.example                        UID u0_a10      GID u0_a10
+    // /data/user/0/com.example/cache                  UID u0_a10      GID u0_a10_cache
+    // /data/media/0/foo.txt                           UID u0_media_rw
+    // /data/media/0/bar.jpg                           UID u0_media_rw GID u0_media_image
+    // /data/media/0/Android/data/com.example          UID u0_media_rw GID u0_a10_ext
+    // /data/media/0/Android/data/com.example/cache    UID u0_media_rw GID u0_a10_ext_cache
+    // /data/media/obb/com.example                     UID system
+
+    struct stats stats;
+    struct stats extStats;
+    memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    ATRACE_BEGIN("obb");
+    for (auto packageName : packageNames) {
+        auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
+        calculate_tree_size(obbCodePath, &extStats.codeSize);
+    }
+    ATRACE_END();
+
+    if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+        ATRACE_BEGIN("code");
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize, -1,
+                    multiuser_get_shared_gid(0, appId));
+        }
+        ATRACE_END();
+
+        ATRACE_BEGIN("quota");
+        collectQuotaStats(device, userId, appId, &stats, &extStats);
+        ATRACE_END();
+    } else {
+        ATRACE_BEGIN("code");
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize);
+        }
+        ATRACE_END();
+
+        for (size_t i = 0; i < packageNames.size(); i++) {
+            const char* pkgname = packageNames[i].c_str();
+
+            ATRACE_BEGIN("data");
+            auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
+            collectManualStats(cePath, &stats);
+            auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
+            collectManualStats(dePath, &stats);
+            ATRACE_END();
+
+            if (!uuid) {
+                ATRACE_BEGIN("profiles");
+                calculate_tree_size(
+                        create_primary_current_profile_package_dir_path(userId, pkgname),
+                        &stats.dataSize);
+                calculate_tree_size(
+                        create_primary_reference_profile_package_dir_path(pkgname),
+                        &stats.codeSize);
+                ATRACE_END();
+            }
+
+            ATRACE_BEGIN("external");
+            auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
+            collectManualStats(extPath, &extStats);
+            auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
+            calculate_tree_size(mediaPath, &extStats.dataSize);
+            ATRACE_END();
+        }
+
+        if (!uuid) {
+            ATRACE_BEGIN("dalvik");
+            int32_t sharedGid = multiuser_get_shared_gid(0, appId);
+            if (sharedGid != -1) {
+                calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                        sharedGid, -1);
+            }
+            ATRACE_END();
+        }
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+        std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId;
+#endif
+
+    struct stats stats;
+    struct stats extStats;
+    memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    if (flags & FLAG_USE_QUOTA) {
+        struct dqblk dq;
+
+        ATRACE_BEGIN("obb");
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << AID_MEDIA_OBB;
+            }
+        } else {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace;
+#endif
+            extStats.codeSize += dq.dqb_curspace;
+        }
+        ATRACE_END();
+
+        ATRACE_BEGIN("code");
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
+        ATRACE_END();
+
+        ATRACE_BEGIN("data");
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats, true);
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats, true);
+        ATRACE_END();
+
+        if (!uuid) {
+            ATRACE_BEGIN("profile");
+            auto userProfilePath = create_primary_cur_profile_dir_path(userId);
+            calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
+            auto refProfilePath = create_primary_ref_profile_dir_path();
+            calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
+            ATRACE_END();
+        }
+
+        ATRACE_BEGIN("external");
+        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+        if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+            }
+        } else {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+            extStats.dataSize += dq.dqb_curspace;
+        }
+        ATRACE_END();
+
+        if (!uuid) {
+            ATRACE_BEGIN("dalvik");
+            calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                    -1, -1, true);
+            calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
+                    -1, -1, true);
+            ATRACE_END();
+        }
+
+        ATRACE_BEGIN("quota");
+        int64_t dataSize = extStats.dataSize;
+        for (auto appId : appIds) {
+            if (appId >= AID_APP_START) {
+                collectQuotaStats(device, userId, appId, &stats, &extStats);
+
+#if MEASURE_DEBUG
+                // Sleep to make sure we don't lose logs
+                usleep(1);
+#endif
+            }
+        }
+        extStats.dataSize = dataSize;
+        ATRACE_END();
+    } else {
+        ATRACE_BEGIN("obb");
+        auto obbPath = create_data_path(uuid_) + "/media/obb";
+        calculate_tree_size(obbPath, &extStats.codeSize);
+        ATRACE_END();
+
+        ATRACE_BEGIN("code");
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
+        ATRACE_END();
+
+        ATRACE_BEGIN("data");
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats);
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats);
+        ATRACE_END();
+
+        if (!uuid) {
+            ATRACE_BEGIN("profile");
+            auto userProfilePath = create_primary_cur_profile_dir_path(userId);
+            calculate_tree_size(userProfilePath, &stats.dataSize);
+            auto refProfilePath = create_primary_ref_profile_dir_path();
+            calculate_tree_size(refProfilePath, &stats.codeSize);
+            ATRACE_END();
+        }
+
+        ATRACE_BEGIN("external");
+        auto dataMediaPath = create_data_media_path(uuid_, userId);
+        collectManualExternalStatsForUser(dataMediaPath, &extStats);
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache "
+                << extStats.cacheSize;
+#endif
+        ATRACE_END();
+
+        if (!uuid) {
+            ATRACE_BEGIN("dalvik");
+            calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
+            calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize);
+            ATRACE_END();
+        }
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+        std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring external " << userId;
+#endif
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    int64_t totalSize = 0;
+    int64_t audioSize = 0;
+    int64_t videoSize = 0;
+    int64_t imageSize = 0;
+    int64_t appSize = 0;
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    if (flags & FLAG_USE_QUOTA) {
+        struct dqblk dq;
+
+        ATRACE_BEGIN("quota");
+        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+        if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+            }
+        } else {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+            totalSize = dq.dqb_curspace;
+        }
+
+        gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace;
+#endif
+            audioSize = dq.dqb_curspace;
+        }
+        gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace;
+#endif
+            videoSize = dq.dqb_curspace;
+        }
+        gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+            LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace;
+#endif
+            imageSize = dq.dqb_curspace;
+        }
+        ATRACE_END();
+
+        ATRACE_BEGIN("apps");
+        struct stats extStats;
+        memset(&extStats, 0, sizeof(extStats));
+        for (auto appId : appIds) {
+            if (appId >= AID_APP_START) {
+                collectQuotaStats(device, userId, appId, nullptr, &extStats);
+            }
+        }
+        appSize = extStats.dataSize;
+        ATRACE_END();
+    } else {
+        ATRACE_BEGIN("manual");
+        FTS *fts;
+        FTSENT *p;
+        auto path = create_data_media_path(uuid_, userId);
+        char *argv[] = { (char*) path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+            return error("Failed to fts_open " + path);
+        }
+        while ((p = fts_read(fts)) != NULL) {
+            char* ext;
+            int64_t size = (p->fts_statp->st_blocks * 512);
+            switch (p->fts_info) {
+            case FTS_F:
+                // Only categorize files not belonging to apps
+                if (p->fts_parent->fts_number == 0) {
+                    ext = strrchr(p->fts_name, '.');
+                    if (ext != nullptr) {
+                        switch (MatchExtension(++ext)) {
+                        case AID_MEDIA_AUDIO: audioSize += size; break;
+                        case AID_MEDIA_VIDEO: videoSize += size; break;
+                        case AID_MEDIA_IMAGE: imageSize += size; break;
+                        }
+                    }
+                }
+                // Fall through to always count against total
+            case FTS_D:
+                // Ignore data belonging to specific apps
+                p->fts_number = p->fts_parent->fts_number;
+                if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) {
+                    p->fts_number = 1;
+                }
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                if (p->fts_parent->fts_number == 1) {
+                    appSize += size;
+                }
+                totalSize += size;
+                break;
+            }
+        }
+        fts_close(fts);
+        ATRACE_END();
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(totalSize);
+    ret.push_back(audioSize);
+    ret.push_back(videoSize);
+    ret.push_back(imageSize);
+    ret.push_back(appSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t appId, int64_t cacheQuota) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
+
+    int32_t uid = multiuser_get_uid(userId, appId);
+    mCacheQuotas[uid] = cacheQuota;
+
+    return ok();
+}
+
+// Dumps the contents of a profile file, using pkgname's dex files for pretty
+// printing the result.
+binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName,
+        const std::string& codePaths, bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* pkgname = packageName.c_str();
+    const char* code_paths = codePaths.c_str();
+
+    *_aidl_return = dump_profiles(uid, pkgname, code_paths);
+    return ok();
+}
+
+// TODO: Consider returning error codes.
+binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
+        bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    *_aidl_return = analyze_primary_profiles(uid, packageName);
+    return ok();
+}
+
+binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
+        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) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (packageName && *packageName != "*") {
+        CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* pkgname = packageName ? packageName->c_str() : "*";
+    const char* instruction_set = instructionSet.c_str();
+    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* 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);
+    return res ? error(res, "Failed to dexopt") : ok();
+}
+
+binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* instruction_set = instructionSet.c_str();
+
+    char boot_marker_path[PKG_PATH_MAX];
+    sprintf(boot_marker_path,
+          "%s/%s/%s/.booting",
+          android_data_dir.path,
+          DALVIK_CACHE,
+          instruction_set);
+
+    ALOGV("mark_boot_complete : %s", boot_marker_path);
+    if (unlink(boot_marker_path) != 0) {
+        return error(StringPrintf("Failed to unlink %s", boot_marker_path));
+    }
+    return ok();
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+        struct stat* statbuf)
+{
+    while (path[basepos] != 0) {
+        if (path[basepos] == '/') {
+            path[basepos] = 0;
+            if (lstat(path, statbuf) < 0) {
+                ALOGV("Making directory: %s\n", path);
+                if (mkdir(path, mode) == 0) {
+                    chown(path, uid, gid);
+                } else {
+                    ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+                }
+            }
+            path[basepos] = '/';
+            basepos++;
+        }
+        basepos++;
+    }
+}
+
+binder::Status InstalldNativeService::linkNativeLibraryDirectory(
+        const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+        const std::string& nativeLibPath32, int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+    const char* asecLibDir = nativeLibPath32.c_str();
+    struct stat s, libStat;
+    binder::Status res = ok();
+
+    auto _pkgdir = create_data_user_ce_package_path(uuid_, userId, pkgname);
+    auto _libsymlink = _pkgdir + PKG_LIB_POSTFIX;
+
+    const char* pkgdir = _pkgdir.c_str();
+    const char* libsymlink = _libsymlink.c_str();
+
+    if (stat(pkgdir, &s) < 0) {
+        return error("Failed to stat " + _pkgdir);
+    }
+
+    if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
+        return error("Failed to chown " + _pkgdir);
+    }
+
+    if (chmod(pkgdir, 0700) < 0) {
+        res = error("Failed to chmod " + _pkgdir);
+        goto out;
+    }
+
+    if (lstat(libsymlink, &libStat) < 0) {
+        if (errno != ENOENT) {
+            res = error("Failed to stat " + _libsymlink);
+            goto out;
+        }
+    } else {
+        if (S_ISDIR(libStat.st_mode)) {
+            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
+                res = error("Failed to delete " + _libsymlink);
+                goto out;
+            }
+        } else if (S_ISLNK(libStat.st_mode)) {
+            if (unlink(libsymlink) < 0) {
+                res = error("Failed to unlink " + _libsymlink);
+                goto out;
+            }
+        }
+    }
+
+    if (symlink(asecLibDir, libsymlink) < 0) {
+        res = error("Failed to symlink " + _libsymlink + " to " + nativeLibPath32);
+        goto out;
+    }
+
+out:
+    if (chmod(pkgdir, s.st_mode) < 0) {
+        auto msg = "Failed to cleanup chmod " + _pkgdir;
+        if (res.isOk()) {
+            res = error(msg);
+        } else {
+            PLOG(ERROR) << msg;
+        }
+    }
+
+    if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
+        auto msg = "Failed to cleanup chown " + _pkgdir;
+        if (res.isOk()) {
+            res = error(msg);
+        } else {
+            PLOG(ERROR) << msg;
+        }
+    }
+
+    return res;
+}
+
+static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+    execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
+            StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+    PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+    execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk,
+            StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+    PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk,
+        const char* idmap_path, int32_t uid) {
+    int idmap_fd = open(idmap_path, O_RDWR);
+    if (idmap_fd < 0) {
+        PLOG(ERROR) << "idmap open failed: " << idmap_path;
+        unlink(idmap_path);
+        return true;
+    }
+
+    pid_t pid;
+    pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        if (setgid(uid) != 0) {
+            LOG(ERROR) << "setgid(" << uid << ") failed during idmap";
+            exit(1);
+        }
+        if (setuid(uid) != 0) {
+            LOG(ERROR) << "setuid(" << uid << ") failed during idmap";
+            exit(1);
+        }
+        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+            PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap";
+            exit(1);
+        }
+
+        run_verify_idmap(target_apk, overlay_apk, idmap_fd);
+        exit(1); /* only if exec call to deleting stale idmap failed */
+    } else {
+        int status = wait_child(pid);
+        close(idmap_fd);
+
+        if (status != 0) {
+            // Failed on verifying if idmap is made from target_apk and overlay_apk.
+            LOG(DEBUG) << "delete stale idmap: " << idmap_path;
+            unlink(idmap_path);
+            return true;
+        }
+    }
+    return false;
+}
+
+// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
+// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
+static int flatten_path(const char *prefix, const char *suffix,
+        const char *overlay_path, char *idmap_path, size_t N)
+{
+    if (overlay_path == NULL || idmap_path == NULL) {
+        return -1;
+    }
+    const size_t len_overlay_path = strlen(overlay_path);
+    // will access overlay_path + 1 further below; requires absolute path
+    if (len_overlay_path < 2 || *overlay_path != '/') {
+        return -1;
+    }
+    const size_t len_idmap_root = strlen(prefix);
+    const size_t len_suffix = strlen(suffix);
+    if (SIZE_MAX - len_idmap_root < len_overlay_path ||
+            SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
+        // additions below would cause overflow
+        return -1;
+    }
+    if (N < len_idmap_root + len_overlay_path + len_suffix) {
+        return -1;
+    }
+    memset(idmap_path, 0, N);
+    snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
+    char *ch = idmap_path + len_idmap_root;
+    while (*ch != '\0') {
+        if (*ch == '/') {
+            *ch = '@';
+        }
+        ++ch;
+    }
+    return 0;
+}
+
+binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
+        const std::string& overlayApkPath, int32_t uid) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* target_apk = targetApkPath.c_str();
+    const char* overlay_apk = overlayApkPath.c_str();
+    ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
+
+    int idmap_fd = -1;
+    char idmap_path[PATH_MAX];
+    struct stat idmap_stat;
+    bool outdated = false;
+
+    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
+                idmap_path, sizeof(idmap_path)) == -1) {
+        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
+        goto fail;
+    }
+
+    if (stat(idmap_path, &idmap_stat) < 0) {
+        outdated = true;
+    } else {
+        outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
+    }
+
+    if (outdated) {
+        idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+    } else {
+        idmap_fd = open(idmap_path, O_RDWR);
+    }
+
+    if (idmap_fd < 0) {
+        ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
+        goto fail;
+    }
+    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
+        ALOGE("idmap cannot chown '%s'\n", idmap_path);
+        goto fail;
+    }
+    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+        ALOGE("idmap cannot chmod '%s'\n", idmap_path);
+        goto fail;
+    }
+
+    if (!outdated) {
+        close(idmap_fd);
+        return ok();
+    }
+
+    pid_t pid;
+    pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        if (setgid(uid) != 0) {
+            ALOGE("setgid(%d) failed during idmap\n", uid);
+            exit(1);
+        }
+        if (setuid(uid) != 0) {
+            ALOGE("setuid(%d) failed during idmap\n", uid);
+            exit(1);
+        }
+        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
+            exit(1);
+        }
+
+        run_idmap(target_apk, overlay_apk, idmap_fd);
+        exit(1); /* only if exec call to idmap failed */
+    } else {
+        int status = wait_child(pid);
+        if (status != 0) {
+            ALOGE("idmap failed, status=0x%04x\n", status);
+            goto fail;
+        }
+    }
+
+    close(idmap_fd);
+    return ok();
+fail:
+    if (idmap_fd >= 0) {
+        close(idmap_fd);
+        unlink(idmap_path);
+    }
+    return error();
+}
+
+binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) {
+    const char* overlay_apk = overlayApkPath.c_str();
+    char idmap_path[PATH_MAX];
+
+    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
+                idmap_path, sizeof(idmap_path)) == -1) {
+        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
+        return error();
+    }
+    if (unlink(idmap_path) < 0) {
+        ALOGE("couldn't unlink idmap file %s\n", idmap_path);
+        return error();
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+        const std::string& seInfo) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    binder::Status res = ok();
+
+    // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+    unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgName = packageName.c_str();
+    const char* seinfo = seInfo.c_str();
+
+    uid_t uid = multiuser_get_uid(userId, appId);
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgName);
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+            res = error("restorecon failed for " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgName);
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+            res = error("restorecon failed for " + path);
+        }
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
+        const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* oat_dir = oatDir.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    char oat_instr_dir[PKG_PATH_MAX];
+
+    if (validate_apk_path(oat_dir)) {
+        return error("Invalid path " + oatDir);
+    }
+    if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+        return error("Failed to prepare " + oatDir);
+    }
+    if (selinux_android_restorecon(oat_dir, 0)) {
+        return error("Failed to restorecon " + oatDir);
+    }
+    snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
+    if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+        return error(StringPrintf("Failed to prepare %s", oat_instr_dir));
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    if (validate_apk_path(packageDir.c_str())) {
+        return error("Invalid path " + packageDir);
+    }
+    if (delete_dir_contents_and_dir(packageDir) != 0) {
+        return error("Failed to delete " + packageDir);
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
+        const std::string& fromBase, const std::string& toBase) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* relative_path = relativePath.c_str();
+    const char* from_base = fromBase.c_str();
+    const char* to_base = toBase.c_str();
+    char from_path[PKG_PATH_MAX];
+    char to_path[PKG_PATH_MAX];
+    snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
+    snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
+
+    if (validate_apk_path_subdirs(from_path)) {
+        return error(StringPrintf("Invalid from path %s", from_path));
+    }
+
+    if (validate_apk_path_subdirs(to_path)) {
+        return error(StringPrintf("Invalid to path %s", to_path));
+    }
+
+    if (link(from_path, to_path) < 0) {
+        return error(StringPrintf("Failed to link from %s to %s", from_path, to_path));
+    }
+
+    return ok();
+}
+
+binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
+        const std::string& instructionSet, const std::string& outputPath) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath.c_str();
+
+    bool success = move_ab(apk_path, instruction_set, oat_dir);
+    return success ? ok() : error();
+}
+
+binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
+        const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+
+    bool res = delete_odex(apk_path, instruction_set, oat_dir);
+    return res ? ok() : error();
+}
+
+binder::Status InstalldNativeService::reconcileSecondaryDexFile(
+        const std::string& dexPath, const std::string& packageName, int32_t uid,
+        const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+        int32_t storage_flag, bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    bool result = android::installd::reconcile_secondary_dex_file(
+            dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
+    return result ? ok() : error();
+}
+
+binder::Status InstalldNativeService::invalidateMounts() {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
+    mStorageMounts.clear();
+    mQuotaReverseMounts.clear();
+
+    std::ifstream in("/proc/mounts");
+    if (!in.is_open()) {
+        return error("Failed to read mounts");
+    }
+
+    std::string source;
+    std::string target;
+    std::string ignored;
+    while (!in.eof()) {
+        std::getline(in, source, ' ');
+        std::getline(in, target, ' ');
+        std::getline(in, ignored);
+
+#if !BYPASS_SDCARDFS
+        if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+            LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+            mStorageMounts[source] = target;
+        }
+#endif
+
+#if !BYPASS_QUOTA
+        if (source.compare(0, 11, "/dev/block/") == 0) {
+            struct dqblk dq;
+            if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
+                    reinterpret_cast<char*>(&dq)) == 0) {
+                LOG(DEBUG) << "Found quota mount " << source << " at " << target;
+                mQuotaReverseMounts[target] = source;
+
+                // ext4 only enables DQUOT_USAGE_ENABLED by default, so we
+                // need to kick it again to enable DQUOT_LIMITS_ENABLED.
+                if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
+                        && errno != EBUSY) {
+                    PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
+                }
+                if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0
+                        && errno != EBUSY) {
+                    PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
+                }
+            }
+        }
+#endif
+    }
+    return ok();
+}
+
+std::string InstalldNativeService::findDataMediaPath(
+        const std::unique_ptr<std::string>& uuid, userid_t userid) {
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
+    auto resolved = mStorageMounts[path];
+    if (resolved.empty()) {
+        LOG(WARNING) << "Failed to find storage mount for " << path;
+        resolved = path;
+    }
+    return StringPrintf("%s/%u", resolved.c_str(), userid);
+}
+
+std::string InstalldNativeService::findQuotaDeviceForUuid(
+        const std::unique_ptr<std::string>& uuid) {
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+    auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+    return mQuotaReverseMounts[path];
+}
+
+binder::Status InstalldNativeService::isQuotaSupported(
+        const std::unique_ptr<std::string>& volumeUuid, bool* _aidl_return) {
+    *_aidl_return = !findQuotaDeviceForUuid(volumeUuid).empty();
+    return ok();
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
new file mode 100644
index 0000000..4011315
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.h
@@ -0,0 +1,142 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef COMMANDS_H_
+#define COMMANDS_H_
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <vector>
+#include <unordered_map>
+
+#include <android-base/macros.h>
+#include <binder/BinderService.h>
+#include <cutils/multiuser.h>
+
+#include "android/os/BnInstalld.h"
+#include "installd_constants.h"
+
+namespace android {
+namespace installd {
+
+class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
+public:
+    static status_t start();
+    static char const* getServiceName() { return "installd"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
+
+    binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+            int32_t userSerial, int32_t flags);
+    binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+            int32_t flags);
+
+    binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+            const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+    binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+            const std::string& seInfo);
+    binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags);
+    binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+    binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+
+    binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
+
+    binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+            const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+            int32_t appId, const std::vector<int64_t>& ceDataInodes,
+            const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
+    binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+            std::vector<int64_t>* _aidl_return);
+    binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+            std::vector<int64_t>* _aidl_return);
+
+    binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t appId, int64_t cacheQuota);
+
+    binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+            const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+            const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+            int32_t targetSdkVersion);
+
+    binder::Status dexopt(const std::string& apkPath, int32_t uid,
+            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);
+
+    binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
+
+    binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return);
+    binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
+            const std::string& codePaths, bool* _aidl_return);
+    binder::Status clearAppProfiles(const std::string& packageName);
+    binder::Status destroyAppProfiles(const std::string& packageName);
+
+    binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
+            int32_t uid);
+    binder::Status removeIdmap(const std::string& overlayApkPath);
+    binder::Status rmPackageDir(const std::string& packageDir);
+    binder::Status markBootComplete(const std::string& instructionSet);
+    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+            int64_t cacheReservedBytes, int32_t flags);
+    binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
+    binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
+    binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
+            const std::string& toBase);
+    binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
+            const std::string& outputPath);
+    binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
+            const std::unique_ptr<std::string>& outputPath);
+    binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
+        const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
+        const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+
+    binder::Status invalidateMounts();
+    binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+            bool* _aidl_return);
+
+private:
+    std::recursive_mutex mLock;
+
+    std::recursive_mutex mMountsLock;
+    std::recursive_mutex mQuotasLock;
+
+    /* Map of all storage mounts from source to target */
+    std::unordered_map<std::string, std::string> mStorageMounts;
+    /* Map of all quota mounts from target to source */
+    std::unordered_map<std::string, std::string> mQuotaReverseMounts;
+
+    /* Map from UID to cache quota size */
+    std::unordered_map<uid_t, int64_t> mCacheQuotas;
+
+    std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid);
+    std::string findQuotaDeviceForUuid(const std::unique_ptr<std::string>& uuid);
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // COMMANDS_H_
diff --git a/cmds/installd/MatchExtensionGen.h b/cmds/installd/MatchExtensionGen.h
new file mode 100644
index 0000000..fded6b7
--- /dev/null
+++ b/cmds/installd/MatchExtensionGen.h
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+
+    switch (ext[0]) {
+    case '3':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case '2':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        }
+    case 'a': case 'A':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'c': case 'C':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'r': case 'R':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 't': case 'T':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'b': case 'B':
+        switch (ext[1]) {
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'c': case 'C':
+        switch (ext[1]) {
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'd': case 'D':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'f': case 'F':
+        switch (ext[1]) {
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'g': case 'G':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'j': case 'J':
+        switch (ext[1]) {
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'l': case 'L':
+        switch (ext[1]) {
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'm': case 'M':
+        switch (ext[1]) {
+        case '3':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case '4':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'k': case 'K':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'i': case 'I':
+                    switch (ext[4]) {
+                    case 'e': case 'E':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '3':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '4':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case 'a': case 'A':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_AUDIO;
+                        }
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'a': case 'A':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'x': case 'X':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'n': case 'N':
+        switch (ext[1]) {
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'o': case 'O':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'p': case 'P':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'c': case 'C':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'q': case 'Q':
+        switch (ext[1]) {
+        case 't': case 'T':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'r': case 'R':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 's': case 'S':
+        switch (ext[1]) {
+        case 'd': case 'D':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'z': case 'Z':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        }
+    case 't': case 'T':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'v': case 'V':
+        switch (ext[1]) {
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'w': case 'W':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case 'm': case 'M':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    }
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'x': case 'X':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
new file mode 100644
index 0000000..f09a397
--- /dev/null
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IInstalld {
+    void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
+    void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+    long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+            int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
+    void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, int appId, @utf8InCpp String seInfo);
+    void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags);
+    void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, long ceDataInode);
+    void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, long ceDataInode);
+
+    void fixupAppData(@nullable @utf8InCpp String uuid, int flags);
+
+    long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+            int userId, int flags, int appId, in long[] ceDataInodes,
+            in @utf8InCpp String[] codePaths);
+    long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+    long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+
+    void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
+
+    void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
+            @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
+            @utf8InCpp String seInfo, int targetSdkVersion);
+
+    void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+            @utf8InCpp String instructionSet, int dexoptNeeded,
+            @nullable @utf8InCpp String outputPath, int dexFlags,
+            @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
+            @nullable @utf8InCpp String sharedLibraries,
+            @nullable @utf8InCpp String seInfo);
+
+    void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
+
+    boolean mergeProfiles(int uid, @utf8InCpp String packageName);
+    boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths);
+    void clearAppProfiles(@utf8InCpp String packageName);
+    void destroyAppProfiles(@utf8InCpp String packageName);
+
+    void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
+    void removeIdmap(@utf8InCpp String overlayApkPath);
+    void rmPackageDir(@utf8InCpp String packageDir);
+    void markBootComplete(@utf8InCpp String instructionSet);
+    void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
+            long cacheReservedBytes, int flags);
+    void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
+            @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
+    void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
+    void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
+            @utf8InCpp String toBase);
+    void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+            @utf8InCpp String outputPath);
+    void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+            @nullable @utf8InCpp String outputPath);
+
+    boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
+        int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
+        int storage_flag);
+
+    void invalidateMounts();
+    boolean isQuotaSupported(@nullable @utf8InCpp String uuid);
+}
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
deleted file mode 100644
index 271c75b..0000000
--- a/cmds/installd/commands.cpp
+++ /dev/null
@@ -1,2294 +0,0 @@
-/*
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include "commands.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <regex>
-#include <stdlib.h>
-#include <sys/capability.h>
-#include <sys/file.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/fs.h>
-#include <cutils/log.h>               // TODO: Move everything to base/logging.
-#include <cutils/sched_policy.h>
-#include <diskusage/dirsize.h>
-#include <logwrap/logwrap.h>
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-#include <system/thread_defs.h>
-
-#include <globals.h>
-#include <installd_deps.h>
-#include <otapreopt_utils.h>
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
-using android::base::EndsWith;
-using android::base::StringPrintf;
-
-namespace android {
-namespace installd {
-
-static constexpr const char* kCpPath = "/system/bin/cp";
-static constexpr const char* kXattrDefault = "user.default";
-
-static constexpr const char* PKG_LIB_POSTFIX = "/lib";
-static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
-static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
-
-static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-static constexpr const char* IDMAP_SUFFIX = "@idmap";
-
-// NOTE: keep in sync with StorageManager
-static constexpr int FLAG_STORAGE_DE = 1 << 0;
-static constexpr int FLAG_STORAGE_CE = 1 << 1;
-
-// NOTE: keep in sync with Installer
-static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-
-/* dexopt needed flags matching those in dalvik.system.DexFile */
-static constexpr int DEXOPT_DEX2OAT_NEEDED       = 1;
-static constexpr int DEXOPT_PATCHOAT_NEEDED      = 2;
-static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
-
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
-
-typedef int fd_t;
-
-static bool property_get_bool(const char* property_name, bool default_value = false) {
-    char tmp_property_value[kPropertyValueMax];
-    bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
-    if (!have_property) {
-        return default_value;
-    }
-    return strcmp(tmp_property_value, "true") == 0;
-}
-
-// Keep profile paths in sync with ActivityThread.
-constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
-static std::string create_primary_profile(const std::string& profile_dir) {
-    return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
-}
-
-/**
- * Perform restorecon of the given path, but only perform recursive restorecon
- * if the label of that top-level file actually changed.  This can save us
- * significant time by avoiding no-op traversals of large filesystem trees.
- */
-static int restorecon_app_data_lazy(const std::string& path, const char* seinfo, uid_t uid) {
-    int res = 0;
-    char* before = nullptr;
-    char* after = nullptr;
-
-    // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
-    // libselinux. Not needed here.
-
-    if (lgetfilecon(path.c_str(), &before) < 0) {
-        PLOG(ERROR) << "Failed before getfilecon for " << path;
-        goto fail;
-    }
-    if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, 0) < 0) {
-        PLOG(ERROR) << "Failed top-level restorecon for " << path;
-        goto fail;
-    }
-    if (lgetfilecon(path.c_str(), &after) < 0) {
-        PLOG(ERROR) << "Failed after getfilecon for " << path;
-        goto fail;
-    }
-
-    // If the initial top-level restorecon above changed the label, then go
-    // back and restorecon everything recursively
-    if (strcmp(before, after)) {
-        LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path
-                << "; running recursive restorecon";
-        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid,
-                SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
-            PLOG(ERROR) << "Failed recursive restorecon for " << path;
-            goto fail;
-        }
-    }
-
-    goto done;
-fail:
-    res = -1;
-done:
-    free(before);
-    free(after);
-    return res;
-}
-
-static int restorecon_app_data_lazy(const std::string& parent, const char* name, const char* seinfo,
-        uid_t uid) {
-    return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seinfo, uid);
-}
-
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
-    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
-        PLOG(ERROR) << "Failed to prepare " << path;
-        return -1;
-    }
-    return 0;
-}
-
-static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode,
-        uid_t uid) {
-    return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid);
-}
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        appid_t appid, const char* seinfo, int target_sdk_version) {
-    uid_t uid = multiuser_get_uid(userid, appid);
-    mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
-        if (prepare_app_dir(path, target_mode, uid) ||
-                prepare_app_dir(path, "cache", 0771, uid) ||
-                prepare_app_dir(path, "code_cache", 0771, uid)) {
-            return -1;
-        }
-
-        // Consider restorecon over contents if label changed
-        if (restorecon_app_data_lazy(path, seinfo, uid) ||
-                restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
-                restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
-            return -1;
-        }
-
-        // Remember inode numbers of cache directories so that we can clear
-        // contents while CE storage is locked
-        if (write_path_inode(path, "cache", kXattrInodeCache) ||
-                write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
-            return -1;
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname);
-        if (prepare_app_dir(path, target_mode, uid)) {
-            // TODO: include result once 25796509 is fixed
-            return 0;
-        }
-
-        // Consider restorecon over contents if label changed
-        if (restorecon_app_data_lazy(path, seinfo, uid)) {
-            return -1;
-        }
-
-        if (property_get_bool("dalvik.vm.usejitprofiles")) {
-            const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
-            // read-write-execute only for the app user.
-            if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << profile_path;
-                return -1;
-            }
-            std::string profile_file = create_primary_profile(profile_path);
-            // read-write only for the app user.
-            if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << profile_path;
-                return -1;
-            }
-            const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
-            // dex2oat/profman runs under the shared app gid and it needs to read/write reference
-            // profiles.
-            appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
-            if (fs_prepare_dir_strict(
-                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
-                return -1;
-            }
-        }
-    }
-    return 0;
-}
-
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
-    // This method only exists to upgrade system apps that have requested
-    // forceDeviceEncrypted, so their default storage always lives in a
-    // consistent location.  This only works on non-FBE devices, since we
-    // never want to risk exposing data on a device with real CE/DE storage.
-
-    auto ce_path = create_data_user_ce_package_path(uuid, userid, pkgname);
-    auto de_path = create_data_user_de_package_path(uuid, userid, pkgname);
-
-    // If neither directory is marked as default, assume CE is default
-    if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
-            && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
-        if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
-            PLOG(ERROR) << "Failed to mark default storage " << ce_path;
-            return -1;
-        }
-    }
-
-    // Migrate default data location if needed
-    auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
-    auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
-
-    if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
-        LOG(WARNING) << "Requested default storage " << target
-                << " is not active; migrating from " << source;
-        if (delete_dir_contents_and_dir(target) != 0) {
-            PLOG(ERROR) << "Failed to delete";
-            return -1;
-        }
-        if (rename(source.c_str(), target.c_str()) != 0) {
-            PLOG(ERROR) << "Failed to rename";
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static bool clear_profile(const std::string& profile) {
-    base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (ufd.get() < 0) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Could not open profile " << profile;
-            return false;
-        } else {
-            // Nothing to clear. That's ok.
-            return true;
-        }
-    }
-
-    if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
-        if (errno != EWOULDBLOCK) {
-            PLOG(WARNING) << "Error locking profile " << profile;
-        }
-        // This implies that the app owning this profile is running
-        // (and has acquired the lock).
-        //
-        // If we can't acquire the lock bail out since clearing is useless anyway
-        // (the app will write again to the profile).
-        //
-        // Note:
-        // This does not impact the this is not an issue for the profiling correctness.
-        // In case this is needed because of an app upgrade, profiles will still be
-        // eventually cleared by the app itself due to checksum mismatch.
-        // If this is needed because profman advised, then keeping the data around
-        // until the next run is again not an issue.
-        //
-        // If the app attempts to acquire a lock while we've held one here,
-        // it will simply skip the current write cycle.
-        return false;
-    }
-
-    bool truncated = ftruncate(ufd.get(), 0) == 0;
-    if (!truncated) {
-        PLOG(WARNING) << "Could not truncate " << profile;
-    }
-    if (flock(ufd.get(), LOCK_UN) != 0) {
-        PLOG(WARNING) << "Error unlocking profile " << profile;
-    }
-    return truncated;
-}
-
-static bool clear_reference_profile(const char* pkgname) {
-    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
-    std::string reference_profile = create_primary_profile(reference_profile_dir);
-    return clear_profile(reference_profile);
-}
-
-static bool clear_current_profile(const char* pkgname, userid_t user) {
-    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
-    std::string profile = create_primary_profile(profile_dir);
-    return clear_profile(profile);
-}
-
-static bool clear_current_profiles(const char* pkgname) {
-    bool success = true;
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        success &= clear_current_profile(pkgname, user);
-    }
-    return success;
-}
-
-int clear_app_profiles(const char* pkgname) {
-    bool success = true;
-    success &= clear_reference_profile(pkgname);
-    success &= clear_current_profiles(pkgname);
-    return success ? 0 : -1;
-}
-
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode) {
-    int res = 0;
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
-        if (flags & FLAG_CLEAR_CACHE_ONLY) {
-            path = read_path_inode(path, "cache", kXattrInodeCache);
-        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
-            path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
-        }
-        if (access(path.c_str(), F_OK) == 0) {
-            res |= delete_dir_contents(path);
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        std::string suffix = "";
-        bool only_cache = false;
-        if (flags & FLAG_CLEAR_CACHE_ONLY) {
-            suffix = CACHE_DIR_POSTFIX;
-            only_cache = true;
-        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
-            suffix = CODE_CACHE_DIR_POSTFIX;
-            only_cache = true;
-        }
-
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix;
-        if (access(path.c_str(), F_OK) == 0) {
-            // TODO: include result once 25796509 is fixed
-            delete_dir_contents(path);
-        }
-        if (!only_cache) {
-            if (!clear_current_profile(pkgname, userid)) {
-                res |= -1;
-            }
-        }
-    }
-    return res;
-}
-
-static int destroy_app_reference_profile(const char *pkgname) {
-    return delete_dir_contents_and_dir(
-        create_data_ref_profile_package_path(pkgname),
-        /*ignore_if_missing*/ true);
-}
-
-static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
-    return delete_dir_contents_and_dir(
-        create_data_user_profile_package_path(userid, pkgname),
-        /*ignore_if_missing*/ true);
-}
-
-int destroy_app_profiles(const char *pkgname) {
-    int result = 0;
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        result |= destroy_app_current_profiles(pkgname, user);
-    }
-    result |= destroy_app_reference_profile(pkgname);
-    return result;
-}
-
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode) {
-    int res = 0;
-    if (flags & FLAG_STORAGE_CE) {
-        res |= delete_dir_contents_and_dir(
-                create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode));
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        res |= delete_dir_contents_and_dir(
-                create_data_user_de_package_path(uuid, userid, pkgname));
-        destroy_app_current_profiles(pkgname, userid);
-        // TODO(calin): If the package is still installed by other users it's probably
-        // beneficial to keep the reference profile around.
-        // Verify if it's ok to do that.
-        destroy_app_reference_profile(pkgname);
-    }
-    return res;
-}
-
-int move_complete_app(const char *from_uuid, const char *to_uuid, const char *package_name,
-        const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version) {
-    std::vector<userid_t> users = get_known_users(from_uuid);
-
-    // Copy app
-    {
-        auto from = create_data_app_package_path(from_uuid, data_app_name);
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        auto to_parent = create_data_app_path(to_uuid);
-
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            (char*) from.c_str(),
-            (char*) to_parent.c_str()
-        };
-
-        LOG(DEBUG) << "Copying " << from << " to " << to;
-        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-
-        if (rc != 0) {
-            LOG(ERROR) << "Failed copying " << from << " to " << to
-                    << ": status " << rc;
-            goto fail;
-        }
-
-        if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            LOG(ERROR) << "Failed to restorecon " << to;
-            goto fail;
-        }
-    }
-
-    // Copy private data for all known users
-    for (auto user : users) {
-
-        // Data source may not exist for all users; that's okay
-        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
-        if (access(from_ce.c_str(), F_OK) != 0) {
-            LOG(INFO) << "Missing source " << from_ce;
-            continue;
-        }
-
-        if (create_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                appid, seinfo, target_sdk_version) != 0) {
-            LOG(ERROR) << "Failed to create package target on " << to_uuid;
-            goto fail;
-        }
-
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            nullptr,
-            nullptr
-        };
-
-        {
-            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_de_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
-
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-            if (rc != 0) {
-                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
-                goto fail;
-            }
-        }
-        {
-            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_ce_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
-
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-            if (rc != 0) {
-                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
-                goto fail;
-            }
-        }
-
-        if (restorecon_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                appid, seinfo) != 0) {
-            LOG(ERROR) << "Failed to restorecon";
-            goto fail;
-        }
-    }
-
-    // We let the framework scan the new location and persist that before
-    // deleting the data in the old location; this ordering ensures that
-    // we can recover from things like battery pulls.
-    return 0;
-
-fail:
-    // Nuke everything we might have already copied
-    {
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-            LOG(WARNING) << "Failed to rollback " << to;
-        }
-    }
-    for (auto user : users) {
-        {
-            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
-            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-                LOG(WARNING) << "Failed to rollback " << to;
-            }
-        }
-        {
-            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
-            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-                LOG(WARNING) << "Failed to rollback " << to;
-            }
-        }
-    }
-    return -1;
-}
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial ATTRIBUTE_UNUSED,
-        int flags) {
-    if (flags & FLAG_STORAGE_DE) {
-        if (uuid == nullptr) {
-            return ensure_config_user_dirs(userid);
-        }
-    }
-    return 0;
-}
-
-int destroy_user_data(const char *uuid, userid_t userid, int flags) {
-    int res = 0;
-    if (flags & FLAG_STORAGE_DE) {
-        res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid, userid), true);
-        if (uuid == nullptr) {
-            res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userid), true);
-            res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userid), true);
-        }
-    }
-    if (flags & FLAG_STORAGE_CE) {
-        res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid, userid), true);
-        res |= delete_dir_contents_and_dir(create_data_media_path(uuid, userid), true);
-    }
-    return res;
-}
-
-/* Try to ensure free_size bytes of storage are available.
- * Returns 0 on success.
- * This is rather simple-minded because doing a full LRU would
- * be potentially memory-intensive, and without atime it would
- * also require that apps constantly modify file metadata even
- * when just reading from the cache, which is pretty awful.
- */
-int free_cache(const char *uuid, int64_t free_size) {
-    cache_t* cache;
-    int64_t avail;
-
-    auto data_path = create_data_path(uuid);
-
-    avail = data_disk_free(data_path);
-    if (avail < 0) return -1;
-
-    ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
-    if (avail >= free_size) return 0;
-
-    cache = start_cache_collection();
-
-    auto users = get_known_users(uuid);
-    for (auto user : users) {
-        add_cache_files(cache, create_data_user_ce_path(uuid, user));
-        add_cache_files(cache, create_data_user_de_path(uuid, user));
-        add_cache_files(cache,
-                StringPrintf("%s/Android/data", create_data_media_path(uuid, user).c_str()));
-    }
-
-    clear_cache_files(data_path, cache, free_size);
-    finish_cache_collection(cache);
-
-    return data_disk_free(data_path) >= free_size ? 0 : -1;
-}
-
-int rm_dex(const char *path, const char *instruction_set)
-{
-    char dex_path[PKG_PATH_MAX];
-
-    if (validate_apk_path(path) && validate_system_app_path(path)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", path);
-        return -1;
-    }
-
-    if (!create_cache_path(dex_path, path, instruction_set)) return -1;
-
-    ALOGV("unlink %s\n", dex_path);
-    if (unlink(dex_path) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));
-        }
-        return -1;
-    } else {
-        return 0;
-    }
-}
-
-static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize,
-        int64_t *cachesize) {
-    DIR *d;
-    int dfd;
-    struct dirent *de;
-    struct stat s;
-
-    d = opendir(path.c_str());
-    if (d == nullptr) {
-        PLOG(WARNING) << "Failed to open " << path;
-        return;
-    }
-    dfd = dirfd(d);
-    while ((de = readdir(d))) {
-        const char *name = de->d_name;
-
-        int64_t statsize = 0;
-        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-            statsize = stat_size(&s);
-        }
-
-        if (de->d_type == DT_DIR) {
-            int subfd;
-            int64_t dirsize = 0;
-            /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
-            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
-            if (subfd >= 0) {
-                dirsize = calculate_dir_size(subfd);
-                close(subfd);
-            }
-            // TODO: check xattrs!
-            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
-                *datasize += statsize;
-                *cachesize += dirsize;
-            } else {
-                *datasize += dirsize + statsize;
-            }
-        } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
-            *codesize += statsize;
-        } else {
-            *datasize += statsize;
-        }
-    }
-    closedir(d);
-}
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-        const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
-        int64_t* asecsize) {
-    DIR *d;
-    int dfd;
-
-    d = opendir(code_path);
-    if (d != nullptr) {
-        dfd = dirfd(d);
-        *codesize += calculate_dir_size(dfd);
-        closedir(d);
-    }
-
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
-        add_app_data_size(path, codesize, datasize, cachesize);
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname);
-        add_app_data_size(path, codesize, datasize, cachesize);
-    }
-
-    *asecsize = 0;
-
-    return 0;
-}
-
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
-        return get_path_inode(path, inode);
-    }
-    return -1;
-}
-
-static int split_count(const char *str)
-{
-  char *ctx;
-  int count = 0;
-  char buf[kPropertyValueMax];
-
-  strncpy(buf, str, sizeof(buf));
-  char *pBuf = buf;
-
-  while(strtok_r(pBuf, " ", &ctx) != NULL) {
-    count++;
-    pBuf = NULL;
-  }
-
-  return count;
-}
-
-static int split(char *buf, const char **argv)
-{
-  char *ctx;
-  int count = 0;
-  char *tok;
-  char *pBuf = buf;
-
-  while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
-    argv[count++] = tok;
-    pBuf = NULL;
-  }
-
-  return count;
-}
-
-static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name,
-    const char* output_file_name, const char *pkgname ATTRIBUTE_UNUSED, const char *instruction_set)
-{
-    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
-    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
-    static const char* PATCHOAT_BIN = "/system/bin/patchoat";
-    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
-        ALOGE("Instruction set %s longer than max length of %d",
-              instruction_set, MAX_INSTRUCTION_SET_LEN);
-        return;
-    }
-
-    /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/
-    char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
-    char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
-    char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN];
-    const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art";
-    // The caller has already gotten all the locks we need.
-    const char* no_lock_arg = "--no-lock-output";
-    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
-    sprintf(output_oat_fd_arg, "--output-oat-fd=%d", oat_fd);
-    sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd);
-    ALOGV("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n",
-          PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name);
-
-    /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
-    char* argv[7];
-    argv[0] = (char*) PATCHOAT_BIN;
-    argv[1] = (char*) patched_image_location_arg;
-    argv[2] = (char*) no_lock_arg;
-    argv[3] = instruction_set_arg;
-    argv[4] = output_oat_fd_arg;
-    argv[5] = input_oat_fd_arg;
-    argv[6] = NULL;
-
-    execv(PATCHOAT_BIN, (char* const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
-}
-
-static void run_dex2oat(int zip_fd, int oat_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 vm_safe_mode, bool debuggable, bool post_bootcomplete,
-        int profile_fd, const char* shared_libraries) {
-    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
-    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
-        ALOGE("Instruction set %s longer than max length of %d",
-              instruction_set, MAX_INSTRUCTION_SET_LEN);
-        return;
-    }
-
-    char dex2oat_Xms_flag[kPropertyValueMax];
-    bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
-
-    char dex2oat_Xmx_flag[kPropertyValueMax];
-    bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
-
-    char dex2oat_threads_buf[kPropertyValueMax];
-    bool have_dex2oat_threads_flag = get_property(post_bootcomplete
-                                                      ? "dalvik.vm.dex2oat-threads"
-                                                      : "dalvik.vm.boot-dex2oat-threads",
-                                                  dex2oat_threads_buf,
-                                                  NULL) > 0;
-    char dex2oat_threads_arg[kPropertyValueMax + 2];
-    if (have_dex2oat_threads_flag) {
-        sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
-    }
-
-    char dex2oat_isa_features_key[kPropertyKeyMax];
-    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
-    char dex2oat_isa_features[kPropertyValueMax];
-    bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
-                                                  dex2oat_isa_features, NULL) > 0;
-
-    char dex2oat_isa_variant_key[kPropertyKeyMax];
-    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
-    char dex2oat_isa_variant[kPropertyValueMax];
-    bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
-                                                 dex2oat_isa_variant, NULL) > 0;
-
-    const char *dex2oat_norelocation = "-Xnorelocate";
-    bool have_dex2oat_relocation_skip_flag = false;
-
-    char dex2oat_flags[kPropertyValueMax];
-    int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
-                                 dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
-    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
-
-    // If we booting without the real /data, don't spend time compiling.
-    char vold_decrypt[kPropertyValueMax];
-    bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
-    bool skip_compilation = (have_vold_decrypt &&
-                             (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
-                             (strcmp(vold_decrypt, "1") == 0)));
-
-    bool generate_debug_info = property_get_bool("debug.generate-debug-info");
-
-    char app_image_format[kPropertyValueMax];
-    char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
-    bool have_app_image_format =
-            image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-    if (have_app_image_format) {
-        sprintf(image_format_arg, "--image-format=%s", app_image_format);
-    }
-
-    char dex2oat_large_app_threshold[kPropertyValueMax];
-    bool have_dex2oat_large_app_threshold =
-            get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
-    char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
-    if (have_dex2oat_large_app_threshold) {
-        sprintf(dex2oat_large_app_threshold_arg,
-                "--very-large-app-threshold=%s",
-                dex2oat_large_app_threshold);
-    }
-
-    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
-
-    static const char* RUNTIME_ARG = "--runtime-arg";
-
-    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
-
-    char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
-    char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
-    char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
-    char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX];
-    char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
-    char instruction_set_variant_arg[strlen("--instruction-set-variant=") + kPropertyValueMax];
-    char instruction_set_features_arg[strlen("--instruction-set-features=") + kPropertyValueMax];
-    char dex2oat_Xms_arg[strlen("-Xms") + kPropertyValueMax];
-    char dex2oat_Xmx_arg[strlen("-Xmx") + kPropertyValueMax];
-    char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
-    bool have_dex2oat_swap_fd = false;
-    char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN];
-    bool have_dex2oat_image_fd = false;
-    char dex2oat_image_fd[strlen("--app-image-fd=") + MAX_INT_LEN];
-
-    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
-    sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
-    sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
-    sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
-    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
-    sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
-    sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
-    if (swap_fd >= 0) {
-        have_dex2oat_swap_fd = true;
-        sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
-    }
-    if (image_fd >= 0) {
-        have_dex2oat_image_fd = true;
-        sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
-    }
-
-    if (have_dex2oat_Xms_flag) {
-        sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
-    }
-    if (have_dex2oat_Xmx_flag) {
-        sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
-    }
-
-    // Compute compiler filter.
-
-    bool have_dex2oat_compiler_filter_flag;
-    if (skip_compilation) {
-        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
-        have_dex2oat_compiler_filter_flag = true;
-        have_dex2oat_relocation_skip_flag = true;
-    } else if (vm_safe_mode) {
-        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
-        have_dex2oat_compiler_filter_flag = true;
-    } else if (compiler_filter != nullptr &&
-            strlen(compiler_filter) + strlen("--compiler-filter=") <
-                    arraysize(dex2oat_compiler_filter_arg)) {
-        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
-        have_dex2oat_compiler_filter_flag = true;
-    } else {
-        char dex2oat_compiler_filter_flag[kPropertyValueMax];
-        have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
-                                                         dex2oat_compiler_filter_flag, NULL) > 0;
-        if (have_dex2oat_compiler_filter_flag) {
-            sprintf(dex2oat_compiler_filter_arg,
-                    "--compiler-filter=%s",
-                    dex2oat_compiler_filter_flag);
-        }
-    }
-
-    // Check whether all apps should be compiled debuggable.
-    if (!debuggable) {
-        char prop_buf[kPropertyValueMax];
-        debuggable =
-                (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
-                (prop_buf[0] == '1');
-    }
-    char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
-    if (profile_fd != -1) {
-        sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
-    }
-
-
-    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
-
-    const char* argv[7  // program name, mandatory arguments and the final NULL
-                     + (have_dex2oat_isa_variant ? 1 : 0)
-                     + (have_dex2oat_isa_features ? 1 : 0)
-                     + (have_dex2oat_Xms_flag ? 2 : 0)
-                     + (have_dex2oat_Xmx_flag ? 2 : 0)
-                     + (have_dex2oat_compiler_filter_flag ? 1 : 0)
-                     + (have_dex2oat_threads_flag ? 1 : 0)
-                     + (have_dex2oat_swap_fd ? 1 : 0)
-                     + (have_dex2oat_image_fd ? 1 : 0)
-                     + (have_dex2oat_relocation_skip_flag ? 2 : 0)
-                     + (generate_debug_info ? 1 : 0)
-                     + (debuggable ? 1 : 0)
-                     + (have_app_image_format ? 1 : 0)
-                     + dex2oat_flags_count
-                     + (profile_fd == -1 ? 0 : 1)
-                     + (shared_libraries != nullptr ? 4 : 0)
-                     + (have_dex2oat_large_app_threshold ? 1 : 0)];
-    int i = 0;
-    argv[i++] = DEX2OAT_BIN;
-    argv[i++] = zip_fd_arg;
-    argv[i++] = zip_location_arg;
-    argv[i++] = oat_fd_arg;
-    argv[i++] = oat_location_arg;
-    argv[i++] = instruction_set_arg;
-    if (have_dex2oat_isa_variant) {
-        argv[i++] = instruction_set_variant_arg;
-    }
-    if (have_dex2oat_isa_features) {
-        argv[i++] = instruction_set_features_arg;
-    }
-    if (have_dex2oat_Xms_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_Xms_arg;
-    }
-    if (have_dex2oat_Xmx_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_Xmx_arg;
-    }
-    if (have_dex2oat_compiler_filter_flag) {
-        argv[i++] = dex2oat_compiler_filter_arg;
-    }
-    if (have_dex2oat_threads_flag) {
-        argv[i++] = dex2oat_threads_arg;
-    }
-    if (have_dex2oat_swap_fd) {
-        argv[i++] = dex2oat_swap_fd;
-    }
-    if (have_dex2oat_image_fd) {
-        argv[i++] = dex2oat_image_fd;
-    }
-    if (generate_debug_info) {
-        argv[i++] = "--generate-debug-info";
-    }
-    if (debuggable) {
-        argv[i++] = "--debuggable";
-    }
-    if (have_app_image_format) {
-        argv[i++] = image_format_arg;
-    }
-    if (have_dex2oat_large_app_threshold) {
-        argv[i++] = dex2oat_large_app_threshold_arg;
-    }
-    if (dex2oat_flags_count) {
-        i += split(dex2oat_flags, argv + i);
-    }
-    if (have_dex2oat_relocation_skip_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_norelocation;
-    }
-    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;
-    }
-    // Do not add after dex2oat_flags, they should override others for debugging.
-    argv[i] = NULL;
-
-    execv(DEX2OAT_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
-}
-
-/*
- * Whether dexopt should use a swap file when compiling an APK.
- *
- * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
- * itself, anyways).
- *
- * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
- *
- * Otherwise, return true if this is a low-mem device.
- *
- * Otherwise, return default value.
- */
-static bool kAlwaysProvideSwapFile = false;
-static bool kDefaultProvideSwapFile = true;
-
-static bool ShouldUseSwapFileForDexopt() {
-    if (kAlwaysProvideSwapFile) {
-        return true;
-    }
-
-    // Check the "override" property. If it exists, return value == "true".
-    char dex2oat_prop_buf[kPropertyValueMax];
-    if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
-        if (strcmp(dex2oat_prop_buf, "true") == 0) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    // Shortcut for default value. This is an implementation optimization for the process sketched
-    // above. If the default value is true, we can avoid to check whether this is a low-mem device,
-    // as low-mem is never returning false. The compiler will optimize this away if it can.
-    if (kDefaultProvideSwapFile) {
-        return true;
-    }
-
-    bool is_low_mem = property_get_bool("ro.config.low_ram");
-    if (is_low_mem) {
-        return true;
-    }
-
-    // Default value must be false here.
-    return kDefaultProvideSwapFile;
-}
-
-static void SetDex2OatAndPatchOatScheduling(bool set_to_bg) {
-    if (set_to_bg) {
-        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-            ALOGE("set_sched_policy failed: %s\n", strerror(errno));
-            exit(70);
-        }
-        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
-            ALOGE("setpriority failed: %s\n", strerror(errno));
-            exit(71);
-        }
-    }
-}
-
-static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
-    for (size_t i = 0; i < fds.size(); i++) {
-        if (close(fds[i]) != 0) {
-            PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
-        }
-    }
-}
-
-static fd_t open_profile_dir(const std::string& profile_dir) {
-    fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
-            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
-    if (profile_dir_fd < 0) {
-        // In a multi-user environment, these directories can be created at
-        // different points and it's possible we'll attempt to open a profile
-        // dir before it exists.
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
-        }
-    }
-    return profile_dir_fd;
-}
-
-static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
-    fd_t profile_dir_fd  = open_profile_dir(profile_dir);
-    if (profile_dir_fd < 0) {
-        return -1;
-    }
-
-    fd_t profile_fd = -1;
-    std::string profile_file = create_primary_profile(profile_dir);
-
-    profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
-    if (profile_fd == -1) {
-        // It's not an error if the profile file does not exist.
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
-        }
-    }
-    // TODO(calin): use AutoCloseFD instead of closing the fd manually.
-    if (close(profile_dir_fd) != 0) {
-        PLOG(WARNING) << "Could not close profile dir " << profile_dir;
-    }
-    return profile_fd;
-}
-
-static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
-    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
-    return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
-}
-
-static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
-    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
-    int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
-    fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
-    if (fd < 0) {
-        return -1;
-    }
-    if (read_write) {
-        // Fix the owner.
-        if (fchown(fd, uid, uid) < 0) {
-            close(fd);
-            return -1;
-        }
-    }
-    return fd;
-}
-
-static void open_profile_files(uid_t uid, const char* pkgname,
-            /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
-    // Open the reference profile in read-write mode as profman might need to save the merge.
-    *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
-    if (*reference_profile_fd < 0) {
-        // We can't access the reference profile file.
-        return;
-    }
-
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        fd_t profile_fd = open_primary_profile_file(user, pkgname);
-        // Add to the lists only if both fds are valid.
-        if (profile_fd >= 0) {
-            profiles_fd->push_back(profile_fd);
-        }
-    }
-}
-
-static void drop_capabilities(uid_t uid) {
-    if (setgid(uid) != 0) {
-        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
-        exit(64);
-    }
-    if (setuid(uid) != 0) {
-        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
-        exit(65);
-    }
-    // drop capabilities
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    if (capset(&capheader, &capdata[0]) < 0) {
-        ALOGE("capset failed: %s\n", strerror(errno));
-        exit(66);
-    }
-}
-
-static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
-
-static void run_profman_merge(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
-    static const size_t MAX_INT_LEN = 32;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
-
-    std::vector<std::string> profile_args(profiles_fd.size());
-    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
-    for (size_t k = 0; k < profiles_fd.size(); k++) {
-        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
-        profile_args[k].assign(profile_buf);
-    }
-    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
-    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
-
-    // program name, reference profile fd, the final NULL and the profile fds
-    const char* argv[3 + profiles_fd.size()];
-    int i = 0;
-    argv[i++] = PROFMAN_BIN;
-    argv[i++] = reference_profile_arg;
-    for (size_t k = 0; k < profile_args.size(); k++) {
-        argv[i++] = profile_args[k].c_str();
-    }
-    // Do not add after dex2oat_flags, they should override others for debugging.
-    argv[i] = NULL;
-
-    execv(PROFMAN_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
-    exit(68);   /* only get here on exec failure */
-}
-
-// Decides if profile guided compilation is needed or not based on existing profiles.
-// Returns true if there is enough information in the current profiles that worth
-// a re-compilation of the package.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-static bool analyse_profiles(uid_t uid, const char* pkgname) {
-    std::vector<fd_t> profiles_fd;
-    fd_t reference_profile_fd = -1;
-    open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
-    if (profiles_fd.empty() || (reference_profile_fd == -1)) {
-        // Skip profile guided compilation because no profiles were found.
-        // Or if the reference profile info couldn't be opened.
-        close_all_fds(profiles_fd, "profiles_fd");
-        if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
-            PLOG(WARNING) << "Failed to close fd for reference profile";
-        }
-        return false;
-    }
-
-    ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-        run_profman_merge(profiles_fd, reference_profile_fd);
-        exit(68);   /* only get here on exec failure */
-    }
-    /* parent */
-    int return_code = wait_child(pid);
-    bool need_to_compile = false;
-    bool should_clear_current_profiles = false;
-    bool should_clear_reference_profile = false;
-    if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
-    } else {
-        return_code = WEXITSTATUS(return_code);
-        switch (return_code) {
-            case PROFMAN_BIN_RETURN_CODE_COMPILE:
-                need_to_compile = true;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = false;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
-                need_to_compile = false;
-                should_clear_current_profiles = false;
-                should_clear_reference_profile = false;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
-                LOG(WARNING) << "Bad profiles for package " << pkgname;
-                need_to_compile = false;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = true;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_ERROR_IO:  // fall-through
-            case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
-                // Temporary IO problem (e.g. locking). Ignore but log a warning.
-                LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
-                need_to_compile = false;
-                should_clear_current_profiles = false;
-                should_clear_reference_profile = false;
-                break;
-           default:
-                // Unknown return code or error. Unlink profiles.
-                LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
-                        << ": " << return_code;
-                need_to_compile = false;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = true;
-                break;
-        }
-    }
-    close_all_fds(profiles_fd, "profiles_fd");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
-    if (should_clear_current_profiles) {
-        clear_current_profiles(pkgname);
-    }
-    if (should_clear_reference_profile) {
-        clear_reference_profile(pkgname);
-    }
-    return need_to_compile;
-}
-
-static void run_profman_dump(const std::vector<fd_t>& profile_fds,
-                             fd_t reference_profile_fd,
-                             const std::vector<std::string>& dex_locations,
-                             const std::vector<fd_t>& apk_fds,
-                             fd_t output_fd) {
-    std::vector<std::string> profman_args;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
-    profman_args.push_back(PROFMAN_BIN);
-    profman_args.push_back("--dump-only");
-    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
-    if (reference_profile_fd != -1) {
-        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
-                                            reference_profile_fd));
-    }
-    for (fd_t profile_fd : profile_fds) {
-        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
-    }
-    for (const std::string& dex_location : dex_locations) {
-        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
-    }
-    for (fd_t apk_fd : apk_fds) {
-        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
-    }
-    const char **argv = new const char*[profman_args.size() + 1];
-    size_t i = 0;
-    for (const std::string& profman_arg : profman_args) {
-        argv[i++] = profman_arg.c_str();
-    }
-    argv[i] = NULL;
-
-    execv(PROFMAN_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
-    exit(68);   /* only get here on exec failure */
-}
-
-static const char* get_location_from_path(const char* path) {
-    static constexpr char kLocationSeparator = '/';
-    const char *location = strrchr(path, kLocationSeparator);
-    if (location == NULL) {
-        return path;
-    } else {
-        // Skip the separator character.
-        return location + 1;
-    }
-}
-
-// Dumps the contents of a profile file, using pkgname's dex files for pretty
-// printing the result.
-bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
-    std::vector<fd_t> profile_fds;
-    fd_t reference_profile_fd = -1;
-    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
-
-    ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
-
-    open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
-
-    const bool has_reference_profile = (reference_profile_fd != -1);
-    const bool has_profiles = !profile_fds.empty();
-
-    if (!has_reference_profile && !has_profiles) {
-        ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
-        return false;
-    }
-
-    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
-    if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
-        ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
-        return false;
-    }
-    std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
-    std::vector<std::string> dex_locations;
-    std::vector<fd_t> apk_fds;
-    for (const std::string& code_full_path : code_full_paths) {
-        const char* full_path = code_full_path.c_str();
-        fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
-        if (apk_fd == -1) {
-            ALOGE("installd cannot open '%s'\n", full_path);
-            return false;
-        }
-        dex_locations.push_back(get_location_from_path(full_path));
-        apk_fds.push_back(apk_fd);
-    }
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
-                         apk_fds, output_fd);
-        exit(68);   /* only get here on exec failure */
-    }
-    /* parent */
-    close_all_fds(apk_fds, "apk_fds");
-    close_all_fds(profile_fds, "profile_fds");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
-    int return_code = wait_child(pid);
-    if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for package " << pkgname << ": "
-                << return_code;
-        return false;
-    }
-    return true;
-}
-
-// Translate the given oat path to an art (app image) path. An empty string
-// denotes an error.
-static std::string create_image_filename(const std::string& oat_path) {
-  // A standard dalvik-cache entry. Replace ".dex" with ".art."
-  if (EndsWith(oat_path, ".dex")) {
-    std::string art_path = oat_path;
-    art_path.replace(art_path.length() - strlen("dex"), strlen("dex"), "art");
-    CHECK(EndsWith(art_path, ".art"));
-    return art_path;
-  }
-
-  // An odex entry. Not that this may not be an extension, e.g., in the OTA
-  // case (where the base name will have an extension for the B artifact).
-  size_t odex_pos = oat_path.rfind(".odex");
-  if (odex_pos != std::string::npos) {
-    std::string art_path = oat_path;
-    art_path.replace(odex_pos, strlen(".odex"), ".art");
-    CHECK_NE(art_path.find(".art"), std::string::npos);
-    return art_path;
-  }
-
-  // Don't know how to handle this.
-  return "";
-}
-
-static bool add_extension_to_file_name(char* file_name, const char* extension) {
-    if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
-        return false;
-    }
-    strcat(file_name, extension);
-    return true;
-}
-
-static int open_output_file(const char* file_name, bool recreate, int permissions) {
-    int flags = O_RDWR | O_CREAT;
-    if (recreate) {
-        if (unlink(file_name) < 0) {
-            if (errno != ENOENT) {
-                PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
-            }
-        }
-        flags |= O_EXCL;
-    }
-    return open(file_name, flags, permissions);
-}
-
-static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
-    if (fchmod(fd,
-               S_IRUSR|S_IWUSR|S_IRGRP |
-               (is_public ? S_IROTH : 0)) < 0) {
-        ALOGE("installd cannot chmod '%s' during dexopt\n", path);
-        return false;
-    } else if (fchown(fd, AID_SYSTEM, uid) < 0) {
-        ALOGE("installd cannot chown '%s' during dexopt\n", path);
-        return false;
-    }
-    return true;
-}
-
-static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
-            const char* oat_dir, /*out*/ char* out_path) {
-    // Early best-effort check whether we can fit the the path into our buffers.
-    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
-    // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
-    // extension to the cache path (5 bytes).
-    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
-        ALOGE("apk_path too long '%s'\n", apk_path);
-        return false;
-    }
-
-    if (oat_dir != NULL && oat_dir[0] != '!') {
-        if (validate_apk_path(oat_dir)) {
-            ALOGE("invalid oat_dir '%s'\n", oat_dir);
-            return false;
-        }
-        if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
-            return false;
-        }
-    } else {
-        if (!create_cache_path(out_path, apk_path, instruction_set)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-// TODO: Consider returning error codes.
-bool merge_profiles(uid_t uid, const char *pkgname) {
-    return analyse_profiles(uid, pkgname);
-}
-
-static const char* parse_null(const char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
-    return dexopt(params[0],                    // apk_path
-                  atoi(params[1]),              // uid
-                  params[2],                    // pkgname
-                  params[3],                    // instruction_set
-                  atoi(params[4]),              // dexopt_needed
-                  params[5],                    // oat_dir
-                  atoi(params[6]),              // dexopt_flags
-                  params[7],                    // compiler_filter
-                  parse_null(params[8]),        // volume_uuid
-                  parse_null(params[9]));       // shared_libraries
-    static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
-}
-
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-//   Dex2oatFileWrapper<std::function<void ()>> file(open(...),
-//                                                   [name]() {
-//                                                       unlink(name.c_str());
-//                                                   });
-//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-//            wrapper if captured as a reference.
-//
-//   if (file.get() == -1) {
-//       // Error opening...
-//   }
-//
-//   ...
-//   if (error) {
-//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-//       // and delete the file (after the fd is closed).
-//       return -1;
-//   }
-//
-//   (Success case)
-//   file.SetCleanup(false);
-//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-//   // (leaving the file around; after the fd is closed).
-//
-template <typename Cleanup>
-class Dex2oatFileWrapper {
- public:
-    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
-    }
-
-    Dex2oatFileWrapper(int value, Cleanup cleanup)
-            : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
-
-    ~Dex2oatFileWrapper() {
-        reset(-1);
-    }
-
-    int get() {
-        return value_;
-    }
-
-    void SetCleanup(bool cleanup) {
-        do_cleanup_ = cleanup;
-    }
-
-    void reset(int new_value) {
-        if (value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-    }
-
-    void reset(int new_value, Cleanup new_cleanup) {
-        if (value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-        cleanup_ = new_cleanup;
-    }
-
- private:
-    int value_;
-    Cleanup cleanup_;
-    bool do_cleanup_;
-};
-
-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 ATTRIBUTE_UNUSED, const char* shared_libraries)
-{
-    bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
-    bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
-    bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
-    bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
-    bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
-
-    // Don't use profile for vm_safe_mode. b/30688277
-    profile_guided = profile_guided && !vm_safe_mode;
-
-    CHECK(pkgname != nullptr);
-    CHECK(pkgname[0] != 0);
-
-    // Public apps should not be compiled with profile information ever. Same goes for the special
-    // package '*' used for the system server.
-    Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
-    if (!is_public && pkgname[0] != '*') {
-        // Open reference profile in read only mode as dex2oat does not get write permissions.
-        const std::string pkgname_str(pkgname);
-        reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
-                                   [pkgname_str]() {
-                                       clear_reference_profile(pkgname_str.c_str());
-                                   });
-        // Note: it's OK to not find a profile here.
-    }
-
-    if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
-        LOG_FATAL("dexopt flags contains unknown fields\n");
-    }
-
-    char out_path[PKG_PATH_MAX];
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
-        return false;
-    }
-
-    const char *input_file;
-    char in_odex_path[PKG_PATH_MAX];
-    switch (dexopt_needed) {
-        case DEXOPT_DEX2OAT_NEEDED:
-            input_file = apk_path;
-            break;
-
-        case DEXOPT_PATCHOAT_NEEDED:
-            if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
-                return -1;
-            }
-            input_file = in_odex_path;
-            break;
-
-        case DEXOPT_SELF_PATCHOAT_NEEDED:
-            input_file = out_path;
-            break;
-
-        default:
-            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            return 72;
-    }
-
-    struct stat input_stat;
-    memset(&input_stat, 0, sizeof(input_stat));
-    stat(input_file, &input_stat);
-
-    base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
-    if (input_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
-        return -1;
-    }
-
-    const std::string out_path_str(out_path);
-    Dex2oatFileWrapper<std::function<void ()>> out_fd(
-            open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
-            [out_path_str]() { unlink(out_path_str.c_str()); });
-    if (out_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
-        return -1;
-    }
-    if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
-        return -1;
-    }
-
-    // Create a swap file if necessary.
-    base::unique_fd swap_fd;
-    if (ShouldUseSwapFileForDexopt()) {
-        // Make sure there really is enough space.
-        char swap_file_name[PKG_PATH_MAX];
-        strcpy(swap_file_name, out_path);
-        if (add_extension_to_file_name(swap_file_name, ".swap")) {
-            swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
-        }
-        if (swap_fd.get() < 0) {
-            // Could not create swap file. Optimistically go on and hope that we can compile
-            // without it.
-            ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
-        } else {
-            // Immediately unlink. We don't really want to hit flash.
-            if (unlink(swap_file_name) < 0) {
-                PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
-            }
-        }
-    }
-
-    // Avoid generating an app image for extract only since it will not contain any classes.
-    Dex2oatFileWrapper<std::function<void ()>> image_fd;
-    const std::string image_path = create_image_filename(out_path);
-    if (!image_path.empty()) {
-        char app_image_format[kPropertyValueMax];
-        bool have_app_image_format =
-                get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-        // 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 && have_app_image_format) {
-            // Recreate is true since we do not want to modify a mapped image. If the app is
-            // already running and we modify the image file, it can cause crashes (b/27493510).
-            image_fd.reset(open_output_file(image_path.c_str(),
-                                            true /*recreate*/,
-                                            0600 /*permissions*/),
-                           [image_path]() { unlink(image_path.c_str()); }
-                           );
-            if (image_fd.get() < 0) {
-                // Could not create application image file. Go on since we can compile without
-                // it.
-                LOG(ERROR) << "installd could not create '"
-                        << image_path
-                        << "' for image file during dexopt";
-            } else if (!set_permissions_and_ownership(image_fd.get(),
-                                                      is_public,
-                                                      uid,
-                                                      image_path.c_str())) {
-                image_fd.reset(-1);
-            }
-        }
-        // If we have a valid image file path but no image fd, explicitly erase the image file.
-        if (image_fd.get() < 0) {
-            if (unlink(image_path.c_str()) < 0) {
-                if (errno != ENOENT) {
-                    PLOG(ERROR) << "Couldn't unlink image file " << image_path;
-                }
-            }
-        }
-    }
-
-    ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-
-        SetDex2OatAndPatchOatScheduling(boot_complete);
-        if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
-            ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
-            _exit(67);
-        }
-
-        if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
-            || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
-            run_patchoat(input_fd.get(),
-                         out_fd.get(),
-                         input_file,
-                         out_path,
-                         pkgname,
-                         instruction_set);
-        } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
-            // Pass dex2oat the relative path to the input file.
-            const char *input_file_name = get_location_from_path(input_file);
-            run_dex2oat(input_fd.get(),
-                        out_fd.get(),
-                        image_fd.get(),
-                        input_file_name,
-                        out_path,
-                        swap_fd.get(),
-                        instruction_set,
-                        compiler_filter,
-                        vm_safe_mode,
-                        debuggable,
-                        boot_complete,
-                        reference_profile_fd.get(),
-                        shared_libraries);
-        } else {
-            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            _exit(73);
-        }
-        _exit(68);   /* only get here on exec failure */
-    } else {
-        int res = wait_child(pid);
-        if (res == 0) {
-            ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
-        } else {
-            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
-            return -1;
-        }
-    }
-
-    struct utimbuf ut;
-    ut.actime = input_stat.st_atime;
-    ut.modtime = input_stat.st_mtime;
-    utime(out_path, &ut);
-
-    // We've been successful, don't delete output.
-    out_fd.SetCleanup(false);
-    image_fd.SetCleanup(false);
-    reference_profile_fd.SetCleanup(false);
-
-    return 0;
-}
-
-int mark_boot_complete(const char* instruction_set)
-{
-  char boot_marker_path[PKG_PATH_MAX];
-  sprintf(boot_marker_path,
-          "%s/%s/%s/.booting",
-          android_data_dir.path,
-          DALVIK_CACHE,
-          instruction_set);
-
-  ALOGV("mark_boot_complete : %s", boot_marker_path);
-  if (unlink(boot_marker_path) != 0) {
-      ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path,
-            strerror(errno));
-      return -1;
-  }
-
-  return 0;
-}
-
-void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
-        struct stat* statbuf)
-{
-    while (path[basepos] != 0) {
-        if (path[basepos] == '/') {
-            path[basepos] = 0;
-            if (lstat(path, statbuf) < 0) {
-                ALOGV("Making directory: %s\n", path);
-                if (mkdir(path, mode) == 0) {
-                    chown(path, uid, gid);
-                } else {
-                    ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
-                }
-            }
-            path[basepos] = '/';
-            basepos++;
-        }
-        basepos++;
-    }
-}
-
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
-{
-    struct stat s, libStat;
-    int rc = 0;
-
-    std::string _pkgdir(create_data_user_ce_package_path(uuid, userId, pkgname));
-    std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
-
-    const char* pkgdir = _pkgdir.c_str();
-    const char* libsymlink = _libsymlink.c_str();
-
-    if (stat(pkgdir, &s) < 0) return -1;
-
-    if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
-        ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
-        return -1;
-    }
-
-    if (chmod(pkgdir, 0700) < 0) {
-        ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
-        rc = -1;
-        goto out;
-    }
-
-    if (lstat(libsymlink, &libStat) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
-            rc = -1;
-            goto out;
-        }
-    } else {
-        if (S_ISDIR(libStat.st_mode)) {
-            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
-                rc = -1;
-                goto out;
-            }
-        } else if (S_ISLNK(libStat.st_mode)) {
-            if (unlink(libsymlink) < 0) {
-                ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));
-                rc = -1;
-                goto out;
-            }
-        }
-    }
-
-    if (symlink(asecLibDir, libsymlink) < 0) {
-        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
-                strerror(errno));
-        rc = -errno;
-        goto out;
-    }
-
-out:
-    if (chmod(pkgdir, s.st_mode) < 0) {
-        ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
-        rc = -errno;
-    }
-
-    if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
-        ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
-        return -errno;
-    }
-
-    return rc;
-}
-
-static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
-    static const char *IDMAP_BIN = "/system/bin/idmap";
-    static const size_t MAX_INT_LEN = 32;
-    char idmap_str[MAX_INT_LEN];
-
-    snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd);
-
-    execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL);
-    ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno));
-}
-
-// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
-// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
-static int flatten_path(const char *prefix, const char *suffix,
-        const char *overlay_path, char *idmap_path, size_t N)
-{
-    if (overlay_path == NULL || idmap_path == NULL) {
-        return -1;
-    }
-    const size_t len_overlay_path = strlen(overlay_path);
-    // will access overlay_path + 1 further below; requires absolute path
-    if (len_overlay_path < 2 || *overlay_path != '/') {
-        return -1;
-    }
-    const size_t len_idmap_root = strlen(prefix);
-    const size_t len_suffix = strlen(suffix);
-    if (SIZE_MAX - len_idmap_root < len_overlay_path ||
-            SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
-        // additions below would cause overflow
-        return -1;
-    }
-    if (N < len_idmap_root + len_overlay_path + len_suffix) {
-        return -1;
-    }
-    memset(idmap_path, 0, N);
-    snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
-    char *ch = idmap_path + len_idmap_root;
-    while (*ch != '\0') {
-        if (*ch == '/') {
-            *ch = '@';
-        }
-        ++ch;
-    }
-    return 0;
-}
-
-int idmap(const char *target_apk, const char *overlay_apk, uid_t uid)
-{
-    ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
-
-    int idmap_fd = -1;
-    char idmap_path[PATH_MAX];
-
-    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
-                idmap_path, sizeof(idmap_path)) == -1) {
-        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
-        goto fail;
-    }
-
-    unlink(idmap_path);
-    idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
-    if (idmap_fd < 0) {
-        ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
-        goto fail;
-    }
-    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
-        ALOGE("idmap cannot chown '%s'\n", idmap_path);
-        goto fail;
-    }
-    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
-        ALOGE("idmap cannot chmod '%s'\n", idmap_path);
-        goto fail;
-    }
-
-    pid_t pid;
-    pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        if (setgid(uid) != 0) {
-            ALOGE("setgid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (setuid(uid) != 0) {
-            ALOGE("setuid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
-            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
-            exit(1);
-        }
-
-        run_idmap(target_apk, overlay_apk, idmap_fd);
-        exit(1); /* only if exec call to idmap failed */
-    } else {
-        int status = wait_child(pid);
-        if (status != 0) {
-            ALOGE("idmap failed, status=0x%04x\n", status);
-            goto fail;
-        }
-    }
-
-    close(idmap_fd);
-    return 0;
-fail:
-    if (idmap_fd >= 0) {
-        close(idmap_fd);
-        unlink(idmap_path);
-    }
-    return -1;
-}
-
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
-        appid_t appid, const char* seinfo) {
-    int res = 0;
-
-    // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
-    unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
-
-    if (!pkgName || !seinfo) {
-        ALOGE("Package name or seinfo tag is null when trying to restorecon.");
-        return -1;
-    }
-
-    uid_t uid = multiuser_get_uid(userid, appid);
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgName);
-        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
-            PLOG(ERROR) << "restorecon failed for " << path;
-            res = -1;
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgName);
-        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
-            PLOG(ERROR) << "restorecon failed for " << path;
-            // TODO: include result once 25796509 is fixed
-        }
-    }
-
-    return res;
-}
-
-int create_oat_dir(const char* oat_dir, const char* instruction_set)
-{
-    char oat_instr_dir[PKG_PATH_MAX];
-
-    if (validate_apk_path(oat_dir)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir);
-        return -1;
-    }
-    if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
-        return -1;
-    }
-    if (selinux_android_restorecon(oat_dir, 0)) {
-        ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno));
-        return -1;
-    }
-    snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
-    if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
-        return -1;
-    }
-    return 0;
-}
-
-int rm_package_dir(const char* apk_path)
-{
-    if (validate_apk_path(apk_path)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
-        return -1;
-    }
-    return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
-}
-
-int link_file(const char* relative_path, const char* from_base, const char* to_base) {
-    char from_path[PKG_PATH_MAX];
-    char to_path[PKG_PATH_MAX];
-    snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
-    snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
-
-    if (validate_apk_path_subdirs(from_path)) {
-        ALOGE("invalid app data sub-path '%s' (bad prefix)\n", from_path);
-        return -1;
-    }
-
-    if (validate_apk_path_subdirs(to_path)) {
-        ALOGE("invalid app data sub-path '%s' (bad prefix)\n", to_path);
-        return -1;
-    }
-
-    const int ret = link(from_path, to_path);
-    if (ret < 0) {
-        ALOGE("link(%s, %s) failed : %s", from_path, to_path, strerror(errno));
-        return -1;
-    }
-
-    return 0;
-}
-
-// Helper for move_ab, so that we can have common failure-case cleanup.
-static bool unlink_and_rename(const char* from, const char* to) {
-    // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
-    // return a failure.
-    struct stat s;
-    if (stat(to, &s) == 0) {
-        if (!S_ISREG(s.st_mode)) {
-            LOG(ERROR) << from << " is not a regular file to replace for A/B.";
-            return false;
-        }
-        if (unlink(to) != 0) {
-            LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
-            return false;
-        }
-    } else {
-        // This may be a permission problem. We could investigate the error code, but we'll just
-        // let the rename failure do the work for us.
-    }
-
-    // Try to rename "to" to "from."
-    if (rename(from, to) != 0) {
-        PLOG(ERROR) << "Could not rename " << from << " to " << to;
-        return false;
-    }
-
-    return true;
-}
-
-// Move/rename a B artifact (from) to an A artifact (to).
-static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
-    // Check whether B exists.
-    {
-        struct stat s;
-        if (stat(b_path.c_str(), &s) != 0) {
-            // Silently ignore for now. The service calling this isn't smart enough to understand
-            // lack of artifacts at the moment.
-            return false;
-        }
-        if (!S_ISREG(s.st_mode)) {
-            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
-            // Try to unlink, but swallow errors.
-            unlink(b_path.c_str());
-            return false;
-        }
-    }
-
-    // Rename B to A.
-    if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
-        // Delete the b_path so we don't try again (or fail earlier).
-        if (unlink(b_path.c_str()) != 0) {
-            PLOG(ERROR) << "Could not unlink " << b_path;
-        }
-
-        return false;
-    }
-
-    return true;
-}
-
-int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
-    if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
-        LOG(ERROR) << "Cannot move_ab with null input";
-        return -1;
-    }
-
-    // Get the current slot suffix. No suffix, no A/B.
-    std::string slot_suffix;
-    {
-        char buf[kPropertyValueMax];
-        if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
-            return -1;
-        }
-        slot_suffix = buf;
-
-        if (!ValidateTargetSlotSuffix(slot_suffix)) {
-            LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
-            return -1;
-        }
-    }
-
-    // Validate other inputs.
-    if (validate_apk_path(apk_path) != 0) {
-        LOG(ERROR) << "invalid apk_path " << apk_path;
-        return -1;
-    }
-    if (validate_apk_path(oat_dir) != 0) {
-        LOG(ERROR) << "invalid oat_dir " << oat_dir;
-        return -1;
-    }
-
-    char a_path[PKG_PATH_MAX];
-    if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
-        return -1;
-    }
-    const std::string a_image_path = create_image_filename(a_path);
-
-    // B path = A path + slot suffix.
-    const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
-    const std::string b_image_path = StringPrintf("%s.%s",
-                                                  a_image_path.c_str(),
-                                                  slot_suffix.c_str());
-
-    bool oat_success = move_ab_path(b_path, a_path);
-    bool success;
-
-    if (oat_success) {
-        // Note: we can live without an app image. As such, ignore failure to move the image file.
-        //       If we decide to require the app image, or the app image being moved correctly,
-        //       then change accordingly.
-        constexpr bool kIgnoreAppImageFailure = true;
-
-        bool art_success = true;
-        if (!a_image_path.empty()) {
-            art_success = move_ab_path(b_image_path, a_image_path);
-            if (!art_success) {
-                unlink(a_image_path.c_str());
-            }
-        }
-
-        success = art_success || kIgnoreAppImageFailure;
-    } else {
-        // Cleanup: delete B image, ignore errors.
-        unlink(b_image_path.c_str());
-
-        success = false;
-    }
-
-    return success ? 0 : -1;
-}
-
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir) {
-    // Delete the oat/odex file.
-    char out_path[PKG_PATH_MAX];
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
-        return false;
-    }
-
-    // In case of a permission failure report the issue. Otherwise just print a warning.
-    auto unlink_and_check = [](const char* path) -> bool {
-        int result = unlink(path);
-        if (result != 0) {
-            if (errno == EACCES || errno == EPERM) {
-                PLOG(ERROR) << "Could not unlink " << path;
-                return false;
-            }
-            PLOG(WARNING) << "Could not unlink " << path;
-        }
-        return true;
-    };
-
-    // Delete the oat/odex file.
-    bool return_value_oat = unlink_and_check(out_path);
-
-    // Derive and delete the app image.
-    bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
-
-    // Report success.
-    return return_value_oat && return_value_art;
-}
-
-}  // namespace installd
-}  // namespace android
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
deleted file mode 100644
index ba27517..0000000
--- a/cmds/installd/commands.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#ifndef COMMANDS_H_
-#define COMMANDS_H_
-
-#include <inttypes.h>
-#include <unistd.h>
-
-#include <cutils/multiuser.h>
-
-#include <installd_constants.h>
-
-namespace android {
-namespace installd {
-
-static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        appid_t appid, const char* seinfo, int target_sdk_version);
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
-        appid_t appid, const char* seinfo);
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode);
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode);
-
-int move_complete_app(const char* from_uuid, const char *to_uuid, const char *package_name,
-        const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version);
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-        const char* code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
-        int64_t *asecsize);
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode);
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial, int flags);
-int destroy_user_data(const char *uuid, userid_t userid, int flags);
-
-int rm_dex(const char *path, const char *instruction_set);
-int free_cache(const char *uuid, int64_t free_size);
-
-bool merge_profiles(uid_t uid, const char *pkgname);
-
-bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
-
-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);
-static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
-
-// Helper for the above, converting arguments.
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
-
-int mark_boot_complete(const char *instruction_set);
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
-int idmap(const char *target_path, const char *overlay_path, uid_t uid);
-int create_oat_dir(const char* oat_dir, const char *instruction_set);
-int rm_package_dir(const char* apk_path);
-int clear_app_profiles(const char* pkgname);
-int destroy_app_profiles(const char* pkgname);
-int link_file(const char *relative_path, const char *from_base, const char *to_base);
-
-// Move a B version over to the A location. Only works for oat_dir != nullptr.
-int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
-
-// Delete odex files generated by dexopt.
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir);
-
-}  // namespace installd
-}  // namespace android
-
-#endif  // COMMANDS_H_
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
new file mode 100644
index 0000000..70cf35c
--- /dev/null
+++ b/cmds/installd/dexopt.cpp
@@ -0,0 +1,1905 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "installed"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>               // TODO: Move everything to base/logging.
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <system/thread_defs.h>
+
+#include "dexopt.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+using android::base::EndsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace installd {
+
+// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
+struct FreeDelete {
+  // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
+  void operator()(const void* ptr) const {
+    free(const_cast<void*>(ptr));
+  }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
+static unique_fd invalid_unique_fd() {
+    return unique_fd(-1);
+}
+
+static bool clear_profile(const std::string& profile) {
+    unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+    if (ufd.get() < 0) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Could not open profile " << profile;
+            return false;
+        } else {
+            // Nothing to clear. That's ok.
+            return true;
+        }
+    }
+
+    if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
+        if (errno != EWOULDBLOCK) {
+            PLOG(WARNING) << "Error locking profile " << profile;
+        }
+        // This implies that the app owning this profile is running
+        // (and has acquired the lock).
+        //
+        // If we can't acquire the lock bail out since clearing is useless anyway
+        // (the app will write again to the profile).
+        //
+        // Note:
+        // This does not impact the this is not an issue for the profiling correctness.
+        // In case this is needed because of an app upgrade, profiles will still be
+        // eventually cleared by the app itself due to checksum mismatch.
+        // If this is needed because profman advised, then keeping the data around
+        // until the next run is again not an issue.
+        //
+        // If the app attempts to acquire a lock while we've held one here,
+        // it will simply skip the current write cycle.
+        return false;
+    }
+
+    bool truncated = ftruncate(ufd.get(), 0) == 0;
+    if (!truncated) {
+        PLOG(WARNING) << "Could not truncate " << profile;
+    }
+    if (flock(ufd.get(), LOCK_UN) != 0) {
+        PLOG(WARNING) << "Error unlocking profile " << profile;
+    }
+    return truncated;
+}
+
+// Clear the reference profile for the given location.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) {
+    return clear_profile(create_reference_profile_path(location, is_secondary_dex));
+}
+
+// Clear the reference profile for the given location.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+static bool clear_current_profile(const std::string& pkgname, userid_t user,
+        bool is_secondary_dex) {
+    return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex));
+}
+
+// Clear the reference profile for the primary apk of the given package.
+bool clear_primary_reference_profile(const std::string& pkgname) {
+    return clear_reference_profile(pkgname, /*is_secondary_dex*/false);
+}
+
+// Clear all current profile for the primary apk of the given package.
+bool clear_primary_current_profiles(const std::string& pkgname) {
+    bool success = true;
+    // For secondary dex files, we don't really need the user but we use it for sanity checks.
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        success &= clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+    }
+    return success;
+}
+
+// Clear the current profile for the primary apk of the given package and user.
+bool clear_primary_current_profile(const std::string& pkgname, userid_t user) {
+    return clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+}
+
+static int split_count(const char *str)
+{
+  char *ctx;
+  int count = 0;
+  char buf[kPropertyValueMax];
+
+  strncpy(buf, str, sizeof(buf));
+  char *pBuf = buf;
+
+  while(strtok_r(pBuf, " ", &ctx) != NULL) {
+    count++;
+    pBuf = NULL;
+  }
+
+  return count;
+}
+
+static int split(char *buf, const char **argv)
+{
+  char *ctx;
+  int count = 0;
+  char *tok;
+  char *pBuf = buf;
+
+  while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
+    argv[count++] = tok;
+    pBuf = NULL;
+  }
+
+  return count;
+}
+
+static const char* get_location_from_path(const char* path) {
+    static constexpr char kLocationSeparator = '/';
+    const char *location = strrchr(path, kLocationSeparator);
+    if (location == NULL) {
+        return path;
+    } else {
+        // Skip the separator character.
+        return location + 1;
+    }
+}
+
+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) {
+    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+        ALOGE("Instruction set %s longer than max length of %d",
+              instruction_set, MAX_INSTRUCTION_SET_LEN);
+        return;
+    }
+
+    // Get the relative path to the input file.
+    const char* relative_input_file_name = get_location_from_path(input_file_name);
+
+    char dex2oat_Xms_flag[kPropertyValueMax];
+    bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
+
+    char dex2oat_Xmx_flag[kPropertyValueMax];
+    bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
+
+    char dex2oat_threads_buf[kPropertyValueMax];
+    bool have_dex2oat_threads_flag = get_property(post_bootcomplete
+                                                      ? "dalvik.vm.dex2oat-threads"
+                                                      : "dalvik.vm.boot-dex2oat-threads",
+                                                  dex2oat_threads_buf,
+                                                  NULL) > 0;
+    char dex2oat_threads_arg[kPropertyValueMax + 2];
+    if (have_dex2oat_threads_flag) {
+        sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
+    }
+
+    char dex2oat_isa_features_key[kPropertyKeyMax];
+    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
+    char dex2oat_isa_features[kPropertyValueMax];
+    bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
+                                                  dex2oat_isa_features, NULL) > 0;
+
+    char dex2oat_isa_variant_key[kPropertyKeyMax];
+    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
+    char dex2oat_isa_variant[kPropertyValueMax];
+    bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
+                                                 dex2oat_isa_variant, NULL) > 0;
+
+    const char *dex2oat_norelocation = "-Xnorelocate";
+    bool have_dex2oat_relocation_skip_flag = false;
+
+    char dex2oat_flags[kPropertyValueMax];
+    int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
+                                 dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
+    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
+
+    // If we are booting without the real /data, don't spend time compiling.
+    char vold_decrypt[kPropertyValueMax];
+    bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
+    bool skip_compilation = (have_vold_decrypt &&
+                             (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
+                             (strcmp(vold_decrypt, "1") == 0)));
+
+    bool generate_debug_info = property_get_bool("debug.generate-debug-info", false);
+
+    char app_image_format[kPropertyValueMax];
+    char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
+    bool have_app_image_format =
+            image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+    if (have_app_image_format) {
+        sprintf(image_format_arg, "--image-format=%s", app_image_format);
+    }
+
+    char dex2oat_large_app_threshold[kPropertyValueMax];
+    bool have_dex2oat_large_app_threshold =
+            get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
+    char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
+    if (have_dex2oat_large_app_threshold) {
+        sprintf(dex2oat_large_app_threshold_arg,
+                "--very-large-app-threshold=%s",
+                dex2oat_large_app_threshold);
+    }
+
+    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+
+    static const char* RUNTIME_ARG = "--runtime-arg";
+
+    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
+
+    // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
+    // use arraysize instead.
+    char zip_fd_arg[arraysize("--zip-fd=") + MAX_INT_LEN];
+    char zip_location_arg[arraysize("--zip-location=") + PKG_PATH_MAX];
+    char input_vdex_fd_arg[arraysize("--input-vdex-fd=") + MAX_INT_LEN];
+    char output_vdex_fd_arg[arraysize("--output-vdex-fd=") + MAX_INT_LEN];
+    char oat_fd_arg[arraysize("--oat-fd=") + MAX_INT_LEN];
+    char oat_location_arg[arraysize("--oat-location=") + PKG_PATH_MAX];
+    char instruction_set_arg[arraysize("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
+    char instruction_set_variant_arg[arraysize("--instruction-set-variant=") + kPropertyValueMax];
+    char instruction_set_features_arg[arraysize("--instruction-set-features=") + kPropertyValueMax];
+    char dex2oat_Xms_arg[arraysize("-Xms") + kPropertyValueMax];
+    char dex2oat_Xmx_arg[arraysize("-Xmx") + kPropertyValueMax];
+    char dex2oat_compiler_filter_arg[arraysize("--compiler-filter=") + kPropertyValueMax];
+    bool have_dex2oat_swap_fd = false;
+    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];
+
+    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
+    sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name);
+    sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd);
+    sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd);
+    sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
+    sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
+    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
+    sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
+    sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
+    if (swap_fd >= 0) {
+        have_dex2oat_swap_fd = true;
+        sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
+    }
+    if (image_fd >= 0) {
+        have_dex2oat_image_fd = true;
+        sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
+    }
+
+    if (have_dex2oat_Xms_flag) {
+        sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
+    }
+    if (have_dex2oat_Xmx_flag) {
+        sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
+    }
+
+    // Compute compiler filter.
+
+    bool have_dex2oat_compiler_filter_flag = false;
+    if (skip_compilation) {
+        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract");
+        have_dex2oat_compiler_filter_flag = true;
+        have_dex2oat_relocation_skip_flag = true;
+    } else if (compiler_filter != nullptr) {
+        if (strlen(compiler_filter) + strlen("--compiler-filter=") <
+                    arraysize(dex2oat_compiler_filter_arg)) {
+            sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+            have_dex2oat_compiler_filter_flag = true;
+        } else {
+            ALOGW("Compiler filter name '%s' is too large (max characters is %zu)",
+                  compiler_filter,
+                  kPropertyValueMax);
+        }
+    }
+
+    if (!have_dex2oat_compiler_filter_flag) {
+        char dex2oat_compiler_filter_flag[kPropertyValueMax];
+        have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
+                                                         dex2oat_compiler_filter_flag, NULL) > 0;
+        if (have_dex2oat_compiler_filter_flag) {
+            sprintf(dex2oat_compiler_filter_arg,
+                    "--compiler-filter=%s",
+                    dex2oat_compiler_filter_flag);
+        }
+    }
+
+    // Check whether all apps should be compiled debuggable.
+    if (!debuggable) {
+        char prop_buf[kPropertyValueMax];
+        debuggable =
+                (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
+                (prop_buf[0] == '1');
+    }
+    char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    if (profile_fd != -1) {
+        sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
+    }
+
+    // Get the directory of the apk to pass as a base classpath directory.
+    char base_dir[arraysize("--classpath-dir=") + PKG_PATH_MAX];
+    std::string apk_dir(input_file_name);
+    unsigned long dir_index = apk_dir.rfind('/');
+    bool has_base_dir = dir_index != std::string::npos;
+    if (has_base_dir) {
+        apk_dir = apk_dir.substr(0, dir_index);
+        sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str());
+    }
+
+
+    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);
+
+    const char* argv[9  // program name, mandatory arguments and the final NULL
+                     + (have_dex2oat_isa_variant ? 1 : 0)
+                     + (have_dex2oat_isa_features ? 1 : 0)
+                     + (have_dex2oat_Xms_flag ? 2 : 0)
+                     + (have_dex2oat_Xmx_flag ? 2 : 0)
+                     + (have_dex2oat_compiler_filter_flag ? 1 : 0)
+                     + (have_dex2oat_threads_flag ? 1 : 0)
+                     + (have_dex2oat_swap_fd ? 1 : 0)
+                     + (have_dex2oat_image_fd ? 1 : 0)
+                     + (have_dex2oat_relocation_skip_flag ? 2 : 0)
+                     + (generate_debug_info ? 1 : 0)
+                     + (debuggable ? 1 : 0)
+                     + (have_app_image_format ? 1 : 0)
+                     + dex2oat_flags_count
+                     + (profile_fd == -1 ? 0 : 1)
+                     + (shared_libraries != nullptr ? 4 : 0)
+                     + (has_base_dir ? 1 : 0)
+                     + (have_dex2oat_large_app_threshold ? 1 : 0)];
+    int i = 0;
+    argv[i++] = DEX2OAT_BIN;
+    argv[i++] = zip_fd_arg;
+    argv[i++] = zip_location_arg;
+    argv[i++] = input_vdex_fd_arg;
+    argv[i++] = output_vdex_fd_arg;
+    argv[i++] = oat_fd_arg;
+    argv[i++] = oat_location_arg;
+    argv[i++] = instruction_set_arg;
+    if (have_dex2oat_isa_variant) {
+        argv[i++] = instruction_set_variant_arg;
+    }
+    if (have_dex2oat_isa_features) {
+        argv[i++] = instruction_set_features_arg;
+    }
+    if (have_dex2oat_Xms_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_Xms_arg;
+    }
+    if (have_dex2oat_Xmx_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_Xmx_arg;
+    }
+    if (have_dex2oat_compiler_filter_flag) {
+        argv[i++] = dex2oat_compiler_filter_arg;
+    }
+    if (have_dex2oat_threads_flag) {
+        argv[i++] = dex2oat_threads_arg;
+    }
+    if (have_dex2oat_swap_fd) {
+        argv[i++] = dex2oat_swap_fd;
+    }
+    if (have_dex2oat_image_fd) {
+        argv[i++] = dex2oat_image_fd;
+    }
+    if (generate_debug_info) {
+        argv[i++] = "--generate-debug-info";
+    }
+    if (debuggable) {
+        argv[i++] = "--debuggable";
+    }
+    if (have_app_image_format) {
+        argv[i++] = image_format_arg;
+    }
+    if (have_dex2oat_large_app_threshold) {
+        argv[i++] = dex2oat_large_app_threshold_arg;
+    }
+    if (dex2oat_flags_count) {
+        i += split(dex2oat_flags, argv + i);
+    }
+    if (have_dex2oat_relocation_skip_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_norelocation;
+    }
+    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;
+    }
+    // Do not add after dex2oat_flags, they should override others for debugging.
+    argv[i] = NULL;
+
+    execv(DEX2OAT_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+}
+
+/*
+ * Whether dexopt should use a swap file when compiling an APK.
+ *
+ * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
+ * itself, anyways).
+ *
+ * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
+ *
+ * Otherwise, return true if this is a low-mem device.
+ *
+ * Otherwise, return default value.
+ */
+static bool kAlwaysProvideSwapFile = false;
+static bool kDefaultProvideSwapFile = true;
+
+static bool ShouldUseSwapFileForDexopt() {
+    if (kAlwaysProvideSwapFile) {
+        return true;
+    }
+
+    // Check the "override" property. If it exists, return value == "true".
+    char dex2oat_prop_buf[kPropertyValueMax];
+    if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
+        if (strcmp(dex2oat_prop_buf, "true") == 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // Shortcut for default value. This is an implementation optimization for the process sketched
+    // above. If the default value is true, we can avoid to check whether this is a low-mem device,
+    // as low-mem is never returning false. The compiler will optimize this away if it can.
+    if (kDefaultProvideSwapFile) {
+        return true;
+    }
+
+    bool is_low_mem = property_get_bool("ro.config.low_ram", false);
+    if (is_low_mem) {
+        return true;
+    }
+
+    // Default value must be false here.
+    return kDefaultProvideSwapFile;
+}
+
+static void SetDex2OatScheduling(bool set_to_bg) {
+    if (set_to_bg) {
+        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+            ALOGE("set_sched_policy failed: %s\n", strerror(errno));
+            exit(70);
+        }
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+            ALOGE("setpriority failed: %s\n", strerror(errno));
+            exit(71);
+        }
+    }
+}
+
+static bool create_profile(int uid, const std::string& profile) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600)));
+    if (fd.get() < 0) {
+        if (errno == EEXIST) {
+            return true;
+        } else {
+            PLOG(ERROR) << "Failed to create profile " << profile;
+            return false;
+        }
+    }
+    // Profiles should belong to the app; make sure of that by giving ownership to
+    // the app uid. If we cannot do that, there's no point in returning the fd
+    // since dex2oat/profman will fail with SElinux denials.
+    if (fchown(fd.get(), uid, uid) < 0) {
+        PLOG(ERROR) << "Could not chwon profile " << profile;
+        return false;
+    }
+    return true;
+}
+
+static unique_fd open_profile(int uid, const std::string& profile, bool read_write) {
+    // Check if we need to open the profile for a read-write operation. If so, we
+    // might need to create the profile since the file might not be there. Reference
+    // profiles are created on the fly so they might not exist beforehand.
+    if (read_write) {
+        if (!create_profile(uid, profile)) {
+            return invalid_unique_fd();
+        }
+    }
+    int flags = read_write ? O_RDWR : O_RDONLY;
+    // Do not follow symlinks when opening a profile:
+    //   - primary profiles should not contain symlinks in their paths
+    //   - secondary dex paths should have been already resolved and validated
+    flags |= O_NOFOLLOW;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+    if (fd.get() < 0) {
+        if (errno != ENOENT) {
+            // Profiles might be missing for various reasons. For example, in a
+            // multi-user environment, the profile directory for one user can be created
+            // after we start a merge. In this case the current profile for that user
+            // will not be found.
+            // Also, the secondary dex profiles might be deleted by the app at any time,
+            // so we can't we need to prepare if they are missing.
+            PLOG(ERROR) << "Failed to open profile " << profile;
+        }
+        return invalid_unique_fd();
+    }
+
+    return fd;
+}
+
+static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,
+        bool is_secondary_dex) {
+    std::string profile = create_current_profile_path(user, location, is_secondary_dex);
+    return open_profile(uid, profile, /*read_write*/false);
+}
+
+static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,
+        bool is_secondary_dex) {
+    std::string profile = create_reference_profile_path(location, is_secondary_dex);
+    return open_profile(uid, profile, read_write);
+}
+
+static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex,
+            /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) {
+    // Open the reference profile in read-write mode as profman might need to save the merge.
+    *reference_profile_fd = open_reference_profile(uid, location, /*read_write*/ true,
+            is_secondary_dex);
+
+    // For secondary dex files, we don't really need the user but we use it for sanity checks.
+    // Note: the user owning the dex file should be the current user.
+    std::vector<userid_t> users;
+    if (is_secondary_dex){
+        users.push_back(multiuser_get_user_id(uid));
+    } else {
+        users = get_known_users(/*volume_uuid*/ nullptr);
+    }
+    for (auto user : users) {
+        unique_fd profile_fd = open_current_profile(uid, user, location, is_secondary_dex);
+        // Add to the lists only if both fds are valid.
+        if (profile_fd.get() >= 0) {
+            profiles_fd->push_back(std::move(profile_fd));
+        }
+    }
+}
+
+static void drop_capabilities(uid_t uid) {
+    if (setgid(uid) != 0) {
+        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
+        exit(64);
+    }
+    if (setuid(uid) != 0) {
+        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
+        exit(65);
+    }
+    // drop capabilities
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    if (capset(&capheader, &capdata[0]) < 0) {
+        ALOGE("capset failed: %s\n", strerror(errno));
+        exit(66);
+    }
+}
+
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+
+static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
+        const unique_fd& reference_profile_fd) {
+    static const size_t MAX_INT_LEN = 32;
+    static const char* PROFMAN_BIN = "/system/bin/profman";
+
+    std::vector<std::string> profile_args(profiles_fd.size());
+    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    for (size_t k = 0; k < profiles_fd.size(); k++) {
+        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get());
+        profile_args[k].assign(profile_buf);
+    }
+    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get());
+
+    // program name, reference profile fd, the final NULL and the profile fds
+    const char* argv[3 + profiles_fd.size()];
+    int i = 0;
+    argv[i++] = PROFMAN_BIN;
+    argv[i++] = reference_profile_arg;
+    for (size_t k = 0; k < profile_args.size(); k++) {
+        argv[i++] = profile_args[k].c_str();
+    }
+    // Do not add after dex2oat_flags, they should override others for debugging.
+    argv[i] = NULL;
+
+    execv(PROFMAN_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    exit(68);   /* only get here on exec failure */
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// The location is the package name for primary apks or the dex path for secondary dex files.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the given location.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+static bool analyze_profiles(uid_t uid, const std::string& location, bool is_secondary_dex) {
+    std::vector<unique_fd> profiles_fd;
+    unique_fd reference_profile_fd;
+    open_profile_files(uid, location, is_secondary_dex, &profiles_fd, &reference_profile_fd);
+    if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
+        // Skip profile guided compilation because no profiles were found.
+        // Or if the reference profile info couldn't be opened.
+        return false;
+    }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        run_profman_merge(profiles_fd, reference_profile_fd);
+        exit(68);   /* only get here on exec failure */
+    }
+    /* parent */
+    int return_code = wait_child(pid);
+    bool need_to_compile = false;
+    bool should_clear_current_profiles = false;
+    bool should_clear_reference_profile = false;
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "profman failed for location " << location << ": " << return_code;
+    } else {
+        return_code = WEXITSTATUS(return_code);
+        switch (return_code) {
+            case PROFMAN_BIN_RETURN_CODE_COMPILE:
+                need_to_compile = true;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+                need_to_compile = false;
+                should_clear_current_profiles = false;
+                should_clear_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
+                LOG(WARNING) << "Bad profiles for location " << location;
+                need_to_compile = false;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = true;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_ERROR_IO:  // fall-through
+            case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
+                // Temporary IO problem (e.g. locking). Ignore but log a warning.
+                LOG(WARNING) << "IO error while reading profiles for location " << location;
+                need_to_compile = false;
+                should_clear_current_profiles = false;
+                should_clear_reference_profile = false;
+                break;
+           default:
+                // Unknown return code or error. Unlink profiles.
+                LOG(WARNING) << "Unknown error code while processing profiles for location "
+                        << location << ": " << return_code;
+                need_to_compile = false;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = true;
+                break;
+        }
+    }
+
+    if (should_clear_current_profiles) {
+        if (is_secondary_dex) {
+            // For secondary dex files, the owning user is the current user.
+            clear_current_profile(location, multiuser_get_user_id(uid), is_secondary_dex);
+        } else  {
+            clear_primary_current_profiles(location);
+        }
+    }
+    if (should_clear_reference_profile) {
+        clear_reference_profile(location, is_secondary_dex);
+    }
+    return need_to_compile;
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// The analysis is done for the primary apks of the given package.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+bool analyze_primary_profiles(uid_t uid, const std::string& pkgname) {
+    return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false);
+}
+
+static void run_profman_dump(const std::vector<unique_fd>& profile_fds,
+                             const unique_fd& reference_profile_fd,
+                             const std::vector<std::string>& dex_locations,
+                             const std::vector<unique_fd>& apk_fds,
+                             const unique_fd& output_fd) {
+    std::vector<std::string> profman_args;
+    static const char* PROFMAN_BIN = "/system/bin/profman";
+    profman_args.push_back(PROFMAN_BIN);
+    profman_args.push_back("--dump-only");
+    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get()));
+    if (reference_profile_fd != -1) {
+        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
+                                            reference_profile_fd.get()));
+    }
+    for (size_t i = 0; i < profile_fds.size(); i++) {
+        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get()));
+    }
+    for (const std::string& dex_location : dex_locations) {
+        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
+    }
+    for (size_t i = 0; i < apk_fds.size(); i++) {
+        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get()));
+    }
+    const char **argv = new const char*[profman_args.size() + 1];
+    size_t i = 0;
+    for (const std::string& profman_arg : profman_args) {
+        argv[i++] = profman_arg.c_str();
+    }
+    argv[i] = NULL;
+
+    execv(PROFMAN_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    exit(68);   /* only get here on exec failure */
+}
+
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) {
+    std::vector<unique_fd> profile_fds;
+    unique_fd reference_profile_fd;
+    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str());
+
+    open_profile_files(uid, pkgname, /*is_secondary_dex*/false,
+            &profile_fds, &reference_profile_fd);
+
+    const bool has_reference_profile = (reference_profile_fd.get() != -1);
+    const bool has_profiles = !profile_fds.empty();
+
+    if (!has_reference_profile && !has_profiles) {
+        LOG(ERROR)  << "profman dump: no profiles to dump for " << pkgname;
+        return false;
+    }
+
+    unique_fd output_fd(open(out_file_name.c_str(),
+            O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644));
+    if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+        ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
+        return false;
+    }
+    std::vector<std::string> code_full_paths = base::Split(code_paths, ";");
+    std::vector<std::string> dex_locations;
+    std::vector<unique_fd> apk_fds;
+    for (const std::string& code_full_path : code_full_paths) {
+        const char* full_path = code_full_path.c_str();
+        unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW));
+        if (apk_fd == -1) {
+            ALOGE("installd cannot open '%s'\n", full_path);
+            return false;
+        }
+        dex_locations.push_back(get_location_from_path(full_path));
+        apk_fds.push_back(std::move(apk_fd));
+    }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
+                         apk_fds, output_fd);
+        exit(68);   /* only get here on exec failure */
+    }
+    /* parent */
+    int return_code = wait_child(pid);
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "profman failed for package " << pkgname << ": "
+                << return_code;
+        return false;
+    }
+    return true;
+}
+
+static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) {
+  // A standard dalvik-cache entry. Replace ".dex" with `new_ext`.
+  if (EndsWith(oat_path, ".dex")) {
+    std::string new_path = oat_path;
+    new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
+    CHECK(EndsWith(new_path, new_ext.c_str()));
+    return new_path;
+  }
+
+  // An odex entry. Not that this may not be an extension, e.g., in the OTA
+  // case (where the base name will have an extension for the B artifact).
+  size_t odex_pos = oat_path.rfind(".odex");
+  if (odex_pos != std::string::npos) {
+    std::string new_path = oat_path;
+    new_path.replace(odex_pos, strlen(".odex"), new_ext);
+    CHECK_NE(new_path.find(new_ext), std::string::npos);
+    return new_path;
+  }
+
+  // Don't know how to handle this.
+  return "";
+}
+
+// Translate the given oat path to an art (app image) path. An empty string
+// denotes an error.
+static std::string create_image_filename(const std::string& oat_path) {
+    return replace_file_extension(oat_path, ".art");
+}
+
+// Translate the given oat path to a vdex path. An empty string denotes an error.
+static std::string create_vdex_filename(const std::string& oat_path) {
+    return replace_file_extension(oat_path, ".vdex");
+}
+
+static bool add_extension_to_file_name(char* file_name, const char* extension) {
+    if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
+        return false;
+    }
+    strcat(file_name, extension);
+    return true;
+}
+
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
+    int flags = O_RDWR | O_CREAT;
+    if (recreate) {
+        if (unlink(file_name) < 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
+            }
+        }
+        flags |= O_EXCL;
+    }
+    return open(file_name, flags, permissions);
+}
+
+static bool set_permissions_and_ownership(
+        int fd, bool is_public, int uid, const char* path, bool is_secondary_dex) {
+    // Primary apks are owned by the system. Secondary dex files are owned by the app.
+    int owning_uid = is_secondary_dex ? uid : AID_SYSTEM;
+    if (fchmod(fd,
+               S_IRUSR|S_IWUSR|S_IRGRP |
+               (is_public ? S_IROTH : 0)) < 0) {
+        ALOGE("installd cannot chmod '%s' during dexopt\n", path);
+        return false;
+    } else if (fchown(fd, owning_uid, uid) < 0) {
+        ALOGE("installd cannot chown '%s' during dexopt\n", path);
+        return false;
+    }
+    return true;
+}
+
+static bool IsOutputDalvikCache(const char* oat_dir) {
+  // InstallerConnection.java (which invokes installd) transforms Java null arguments
+  // into '!'. Play it safe by handling it both.
+  // TODO: ensure we never get null.
+  // TODO: pass a flag instead of inferring if the output is dalvik cache.
+  return oat_dir == nullptr || oat_dir[0] == '!';
+}
+
+static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
+            const char* oat_dir, bool is_secondary_dex, /*out*/ char* out_oat_path) {
+    // Early best-effort check whether we can fit the the path into our buffers.
+    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
+    // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
+    // extension to the cache path (5 bytes).
+    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+        ALOGE("apk_path too long '%s'\n", apk_path);
+        return false;
+    }
+
+    if (!IsOutputDalvikCache(oat_dir)) {
+        // Oat dirs for secondary dex files are already validated.
+        if (!is_secondary_dex && validate_apk_path(oat_dir)) {
+            ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir);
+            return false;
+        }
+        if (!calculate_oat_file_path(out_oat_path, oat_dir, apk_path, instruction_set)) {
+            return false;
+        }
+    } else {
+        if (!create_cache_path(out_oat_path, apk_path, instruction_set)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
+// on destruction. It will also run the given cleanup (unless told not to) after closing.
+//
+// Usage example:
+//
+//   Dex2oatFileWrapper file(open(...),
+//                                                   [name]() {
+//                                                       unlink(name.c_str());
+//                                                   });
+//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
+//            wrapper if captured as a reference.
+//
+//   if (file.get() == -1) {
+//       // Error opening...
+//   }
+//
+//   ...
+//   if (error) {
+//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
+//       // and delete the file (after the fd is closed).
+//       return -1;
+//   }
+//
+//   (Success case)
+//   file.SetCleanup(false);
+//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
+//   // (leaving the file around; after the fd is closed).
+//
+class Dex2oatFileWrapper {
+ public:
+    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
+    }
+
+    Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
+            : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+    Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
+        value_ = other.value_;
+        cleanup_ = other.cleanup_;
+        do_cleanup_ = other.do_cleanup_;
+        auto_close_ = other.auto_close_;
+        other.release();
+    }
+
+    Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
+        value_ = other.value_;
+        cleanup_ = other.cleanup_;
+        do_cleanup_ = other.do_cleanup_;
+        auto_close_ = other.auto_close_;
+        other.release();
+        return *this;
+    }
+
+    ~Dex2oatFileWrapper() {
+        reset(-1);
+    }
+
+    int get() {
+        return value_;
+    }
+
+    void SetCleanup(bool cleanup) {
+        do_cleanup_ = cleanup;
+    }
+
+    void reset(int new_value) {
+        if (auto_close_ && value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+    }
+
+    void reset(int new_value, std::function<void ()> new_cleanup) {
+        if (auto_close_ && value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+        cleanup_ = new_cleanup;
+    }
+
+    void DisableAutoClose() {
+        auto_close_ = false;
+    }
+
+ private:
+    void release() {
+        value_ = -1;
+        do_cleanup_ = false;
+        cleanup_ = nullptr;
+    }
+    int value_;
+    std::function<void ()> cleanup_;
+    bool do_cleanup_;
+    bool auto_close_;
+};
+
+// (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) {
+        return Dex2oatFileWrapper();
+    }
+
+    const std::string image_path = create_image_filename(out_oat_path);
+    if (image_path.empty()) {
+        // Happens when the out_oat_path has an unknown extension.
+        return Dex2oatFileWrapper();
+    }
+    char app_image_format[kPropertyValueMax];
+    bool have_app_image_format =
+            get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+    if (!have_app_image_format) {
+        return Dex2oatFileWrapper();
+    }
+    // Recreate is true since we do not want to modify a mapped image. If the app is
+    // already running and we modify the image file, it can cause crashes (b/27493510).
+    Dex2oatFileWrapper wrapper_fd(
+            open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
+            [image_path]() { unlink(image_path.c_str()); });
+    if (wrapper_fd.get() < 0) {
+        // Could not create application image file. Go on since we can compile without it.
+        LOG(ERROR) << "installd could not create '" << image_path
+                << "' for image file during dexopt";
+         // If we have a valid image file path but no image fd, explicitly erase the image file.
+        if (unlink(image_path.c_str()) < 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+            }
+        }
+    } else if (!set_permissions_and_ownership(
+                wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
+        ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
+        wrapper_fd.reset(-1);
+    }
+
+    return wrapper_fd;
+}
+
+// Creates the dexopt swap file if necessary and return its fd.
+// Returns -1 if there's no need for a swap or in case of errors.
+unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+    if (!ShouldUseSwapFileForDexopt()) {
+        return invalid_unique_fd();
+    }
+    // Make sure there really is enough space.
+    char swap_file_name[PKG_PATH_MAX];
+    strcpy(swap_file_name, out_oat_path);
+    if (!add_extension_to_file_name(swap_file_name, ".swap")) {
+        return invalid_unique_fd();
+    }
+    unique_fd swap_fd(open_output_file(
+            swap_file_name, /*recreate*/true, /*permissions*/0600));
+    if (swap_fd.get() < 0) {
+        // Could not create swap file. Optimistically go on and hope that we can compile
+        // without it.
+        ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
+    } else {
+        // Immediately unlink. We don't really want to hit flash.
+        if (unlink(swap_file_name) < 0) {
+            PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
+        }
+    }
+    return swap_fd;
+}
+
+// Opens the reference profiles if needed.
+// Note that the reference profile might not exist so it's OK if the fd will be -1.
+Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
+        const std::string& dex_path, bool profile_guided, bool is_public, int uid,
+        bool is_secondary_dex) {
+    // Public apps should not be compiled with profile information ever. Same goes for the special
+    // package '*' used for the system server.
+    if (!profile_guided || is_public || (pkgname[0] == '*')) {
+        return Dex2oatFileWrapper();
+    }
+
+    // Open reference profile in read only mode as dex2oat does not get write permissions.
+    const std::string location = is_secondary_dex ? dex_path : pkgname;
+    unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex);
+    const auto& cleanup = [location, is_secondary_dex]() {
+        clear_reference_profile(location.c_str(), is_secondary_dex);
+    };
+    return Dex2oatFileWrapper(ufd.release(), cleanup);
+}
+
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
+// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
+        const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
+        bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
+        Dex2oatFileWrapper* out_vdex_wrapper_fd) {
+    CHECK(in_vdex_wrapper_fd != nullptr);
+    CHECK(out_vdex_wrapper_fd != nullptr);
+    // Open the existing VDEX. We do this before creating the new output VDEX, which will
+    // unlink the old one.
+    char in_odex_path[PKG_PATH_MAX];
+    int dexopt_action = abs(dexopt_needed);
+    bool is_odex_location = dexopt_needed < 0;
+    std::string in_vdex_path_str;
+
+    // Infer the name of the output VDEX.
+    const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
+    if (out_vdex_path_str.empty()) {
+        return false;
+    }
+
+    bool update_vdex_in_place = false;
+    if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+        // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
+        const char* path = nullptr;
+        if (is_odex_location) {
+            if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+                path = in_odex_path;
+            } else {
+                ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path);
+                return false;
+            }
+        } else {
+            path = out_oat_path;
+        }
+        in_vdex_path_str = create_vdex_filename(path);
+        if (in_vdex_path_str.empty()) {
+            ALOGE("installd cannot compute input vdex location for '%s'\n", path);
+            return false;
+        }
+        // We can update in place when all these conditions are met:
+        // 1) The vdex location to write to is the same as the vdex location to read (vdex files
+        //    on /system typically cannot be updated in place).
+        // 2) We dex2oat due to boot image change, because we then know the existing vdex file
+        //    cannot be currently used by a running process.
+        // 3) We are not doing a profile guided compilation, because dexlayout requires two
+        //    different vdex files to operate.
+        update_vdex_in_place =
+            (in_vdex_path_str == out_vdex_path_str) &&
+            (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
+            !profile_guided;
+        if (update_vdex_in_place) {
+            // Open the file read-write to be able to update it.
+            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
+            if (in_vdex_wrapper_fd->get() == -1) {
+                // If we failed to open the file, we cannot update it in place.
+                update_vdex_in_place = false;
+            }
+        } else {
+            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+        }
+    }
+
+    // If we are updating the vdex in place, we do not need to recreate a vdex,
+    // and can use the same existing one.
+    if (update_vdex_in_place) {
+        // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
+        // have bogus stale vdex files.
+        out_vdex_wrapper_fd->reset(
+              in_vdex_wrapper_fd->get(),
+              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        // Disable auto close for the in wrapper fd (it will be done when destructing the out
+        // wrapper).
+        in_vdex_wrapper_fd->DisableAutoClose();
+    } else {
+        out_vdex_wrapper_fd->reset(
+              open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
+              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        if (out_vdex_wrapper_fd->get() < 0) {
+            ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
+            return false;
+        }
+    }
+    if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+            out_vdex_path_str.c_str(), is_secondary_dex)) {
+        ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
+        return false;
+    }
+
+    // If we got here we successfully opened the vdex files.
+    return true;
+}
+
+// Opens the output oat file for the given apk.
+// If successful it stores the output path into out_oat_path and returns true.
+Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
+        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
+        char* out_oat_path) {
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
+        return Dex2oatFileWrapper();
+    }
+    const std::string out_oat_path_str(out_oat_path);
+    Dex2oatFileWrapper wrapper_fd(
+            open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
+            [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
+    if (wrapper_fd.get() < 0) {
+        PLOG(ERROR) << "installd cannot open output during dexopt" <<  out_oat_path;
+    } else if (!set_permissions_and_ownership(
+                wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+        ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
+        wrapper_fd.reset(-1);
+    }
+    return wrapper_fd;
+}
+
+// Updates the access times of out_oat_path based on those from apk_path.
+void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) {
+    struct stat input_stat;
+    memset(&input_stat, 0, sizeof(input_stat));
+    if (stat(apk_path, &input_stat) != 0) {
+        PLOG(ERROR) << "Could not stat " << apk_path << " during dexopt";
+        return;
+    }
+
+    struct utimbuf ut;
+    ut.actime = input_stat.st_atime;
+    ut.modtime = input_stat.st_mtime;
+    if (utime(out_oat_path, &ut) != 0) {
+        PLOG(WARNING) << "Could not update access times for " << apk_path << " during dexopt";
+    }
+}
+
+// Runs (execv) dexoptanalyzer on the given arguments.
+// The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter.
+// 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 char* instruction_set,
+        const char* compiler_filter, bool profile_was_updated) {
+    static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+        ALOGE("Instruction set %s longer than max length of %d",
+              instruction_set, MAX_INSTRUCTION_SET_LEN);
+        return;
+    }
+
+    char dex_file_arg[strlen("--dex-file=") + PKG_PATH_MAX];
+    char isa_arg[strlen("--isa=") + MAX_INSTRUCTION_SET_LEN];
+    char compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
+    const char* assume_profile_changed = "--assume-profile-changed";
+
+    sprintf(dex_file_arg, "--dex-file=%s", dex_file.c_str());
+    sprintf(isa_arg, "--isa=%s", instruction_set);
+    sprintf(compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+
+    // program name, dex file, isa, filter, the final NULL
+    const char* argv[5 + (profile_was_updated ? 1 : 0)];
+    int i = 0;
+    argv[i++] = DEXOPTANALYZER_BIN;
+    argv[i++] = dex_file_arg;
+    argv[i++] = isa_arg;
+    argv[i++] = compiler_filter_arg;
+    if (profile_was_updated) {
+        argv[i++] = assume_profile_changed;
+    }
+    argv[i] = NULL;
+
+    execv(DEXOPTANALYZER_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+}
+
+// Prepares the oat dir for the secondary dex files.
+static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
+        const char* instruction_set, std::string* oat_dir_out) {
+    unsigned long dirIndex = dex_path.rfind('/');
+    if (dirIndex == std::string::npos) {
+        LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
+        return false;
+    }
+    std::string dex_dir = dex_path.substr(0, dirIndex);
+
+    // Create oat file output directory.
+    mode_t oat_dir_mode = S_IRWXU | S_IRWXG | S_IXOTH;
+    if (prepare_app_cache_dir(dex_dir, "oat", oat_dir_mode, uid, uid) != 0) {
+        LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << dex_path;
+        return false;
+    }
+
+    char oat_dir[PKG_PATH_MAX];
+    snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str());
+    oat_dir_out->assign(oat_dir);
+
+    // Create oat/isa output directory.
+    if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) {
+        LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
+        return false;
+    }
+
+    return true;
+}
+
+static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+
+// Verifies the result of dexoptanalyzer executed for the apk_path.
+// If the result is valid returns true and sets dexopt_needed_out to a valid value.
+// Returns false for errors or unexpected result values.
+static bool process_dexoptanalyzer_result(const std::string& dex_path, int result,
+            int* dexopt_needed_out) {
+    // The result values are defined in dexoptanalyzer.
+    switch (result) {
+        case 0:  // no_dexopt_needed
+            *dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
+        case 1:  // dex2oat_from_scratch
+            *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
+        case 5:  // dex2oat_for_bootimage_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
+        case 6:  // dex2oat_for_filter_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
+        case 7:  // dex2oat_for_relocation_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
+        case 2:  // dex2oat_for_bootimage_oat
+        case 3:  // dex2oat_for_filter_oat
+        case 4:  // dex2oat_for_relocation_oat
+            LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
+                    << " Expected odex file status for secondary dex " << dex_path
+                    << " : dexoptanalyzer result=" << result;
+            return false;
+        default:
+            LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
+                    << " exec_dexoptanalyzer result=" << result;
+            return false;
+    }
+}
+
+// Processes the dex_path as a secondary dex files and return true if the path dex file should
+// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
+// successfully.
+// When returning true, the output parameters will be:
+//   - is_public_out: whether or not the oat file should not be made public
+//   - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
+//   - oat_dir_out: the oat dir path where the oat file should be stored
+//   - dex_path_out: the real path of the dex file
+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) {
+    int storage_flag;
+
+    if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+        storage_flag = FLAG_STORAGE_CE;
+        if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+            LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+            return false;
+        }
+    } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+        storage_flag = FLAG_STORAGE_DE;
+    } else {
+        LOG(ERROR) << "Secondary dex storage flag must be set";
+        return false;
+    }
+
+    {
+        // As opposed to the primary apk, secondary dex files might contain symlinks.
+        // Resolve the path before passing it to the validate method to
+        // make sure the verification is done on the real location.
+        UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr));
+        if (dex_real_path_cstr == nullptr) {
+            PLOG(ERROR) << "Could not get the real path of the secondary dex file "
+                    << original_dex_path;
+            return false;
+        } else {
+            dex_path_out->assign(dex_real_path_cstr.get());
+        }
+    }
+    const std::string& dex_path = *dex_path_out;
+    if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+        return false;
+    }
+
+    // Check if the path exist. If not, there's nothing to do.
+    struct stat dex_path_stat;
+    if (stat(dex_path.c_str(), &dex_path_stat) != 0) {
+        if (errno == ENOENT) {
+            // Secondary dex files might be deleted any time by the app.
+            // Nothing to do if that's the case
+            ALOGV("Secondary dex does not exist %s", dex_path.c_str());
+            return NO_DEXOPT_NEEDED;
+        } else {
+            PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+        }
+    }
+
+    // Check if we should make the oat file public.
+    // Note that if the dex file is not public the compiled code cannot be made public.
+    *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) &&
+            ((dex_path_stat.st_mode & S_IROTH) != 0);
+
+    // Prepare the oat directories.
+    if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) {
+        return false;
+    }
+
+    // Analyze profiles.
+    bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        // 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);
+        exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+    }
+
+    /* parent */
+
+    int result = wait_child(pid);
+    if (!WIFEXITED(result)) {
+        LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
+        return false;
+    }
+    result = WEXITSTATUS(result);
+    bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+    // Run dexopt only if needed or forced.
+    // Note that dexoptanalyzer is executed even if force compilation is enabled.
+    // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
+    // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
+    // for oat files from dalvik-cache.
+    if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+        *dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
+    }
+
+    return success;
+}
+
+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) {
+    CHECK(pkgname != nullptr);
+    CHECK(pkgname[0] != 0);
+    if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
+        LOG_FATAL("dexopt flags contains unknown fields\n");
+    }
+
+    bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
+    bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
+    bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
+    bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+    bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+
+    // Check if we're dealing with a secondary dex file and if we need to compile it.
+    std::string oat_dir_str;
+    std::string dex_real_path;
+    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)) {
+            oat_dir = oat_dir_str.c_str();
+            dex_path = dex_real_path.c_str();
+            if (dexopt_needed == NO_DEXOPT_NEEDED) {
+                return 0;  // Nothing to do, report success.
+            }
+        } else {
+            return -1;  // We had an error, logged in the process method.
+        }
+    } else {
+        // Currently these flags are only use for secondary dex files.
+        // Verify that they are not set for primary apks.
+        CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0);
+        CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0);
+    }
+
+    // Open the input file.
+    unique_fd input_fd(open(dex_path, O_RDONLY, 0));
+    if (input_fd.get() < 0) {
+        ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path);
+        return -1;
+    }
+
+    // Create the output OAT file.
+    char out_oat_path[PKG_PATH_MAX];
+    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+            instruction_set, is_secondary_dex, out_oat_path);
+    if (out_oat_fd.get() < 0) {
+        return -1;
+    }
+
+    // Open vdex files.
+    Dex2oatFileWrapper in_vdex_fd;
+    Dex2oatFileWrapper out_vdex_fd;
+    if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
+            is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+        return -1;
+    }
+
+    // Ensure that the oat dir and the compiler artifacts of secondary dex files have the correct
+    // selinux context (we generate them on the fly during the dexopt invocation and they don't
+    // fully inherit their parent context).
+    // Note that for primary apk the oat files are created before, in a separate installd
+    // call which also does the restorecon. TODO(calin): unify the paths.
+    if (is_secondary_dex) {
+        if (selinux_android_restorecon_pkgdir(oat_dir, se_info, uid,
+                SELINUX_ANDROID_RESTORECON_RECURSE)) {
+            LOG(ERROR) << "Failed to restorecon " << oat_dir;
+            return -1;
+        }
+    }
+
+    // Create a swap file if necessary.
+    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+
+    // Create the app image file if needed.
+    Dex2oatFileWrapper image_fd =
+            maybe_open_app_image(out_oat_path, profile_guided, is_public, uid, is_secondary_dex);
+
+    // Open the reference profile if needed.
+    Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+            pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex);
+
+    ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path);
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+
+        SetDex2OatScheduling(boot_complete);
+        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
+            ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno));
+            _exit(67);
+        }
+
+        run_dex2oat(input_fd.get(),
+                    out_oat_fd.get(),
+                    in_vdex_fd.get(),
+                    out_vdex_fd.get(),
+                    image_fd.get(),
+                    dex_path,
+                    out_oat_path,
+                    swap_fd.get(),
+                    instruction_set,
+                    compiler_filter,
+                    debuggable,
+                    boot_complete,
+                    reference_profile_fd.get(),
+                    shared_libraries);
+        _exit(68);   /* only get here on exec failure */
+    } else {
+        int res = wait_child(pid);
+        if (res == 0) {
+            ALOGV("DexInv: --- END '%s' (success) ---\n", dex_path);
+        } else {
+            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", dex_path, res);
+            return res;
+        }
+    }
+
+    update_out_oat_access_times(dex_path, out_oat_path);
+
+    // We've been successful, don't delete output.
+    out_oat_fd.SetCleanup(false);
+    out_vdex_fd.SetCleanup(false);
+    image_fd.SetCleanup(false);
+    reference_profile_fd.SetCleanup(false);
+
+    return 0;
+}
+
+// Try to remove the given directory. Log an error if the directory exists
+// and is empty but could not be removed.
+static bool rmdir_if_empty(const char* dir) {
+    if (rmdir(dir) == 0) {
+        return true;
+    }
+    if (errno == ENOENT || errno == ENOTEMPTY) {
+        return true;
+    }
+    PLOG(ERROR) << "Failed to remove dir: " << dir;
+    return false;
+}
+
+// Try to unlink the given file. Log an error if the file exists and could not
+// be unlinked.
+static bool unlink_if_exists(const std::string& file) {
+    if (unlink(file.c_str()) == 0) {
+        return true;
+    }
+    if (errno == ENOENT) {
+        return true;
+
+    }
+    PLOG(ERROR) << "Could not unlink: " << file;
+    return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+        /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
+    size_t dirIndex = dex_path.rfind('/');
+    if (dirIndex == std::string::npos) {
+        LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+        return false;
+    }
+    // TODO(calin): we have similar computations in at lest 3 other places
+    // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+    // use string append.
+    std::string apk_dir = dex_path.substr(0, dirIndex);
+    snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+    snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+    if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+            /*is_secondary_dex*/true, out_oat_path)) {
+        LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+        return false;
+    }
+    return true;
+}
+
+// Reconcile the secondary dex 'dex_path' and its generated oat files.
+// Return true if all the parameters are valid and the secondary dex file was
+//   processed successfully (i.e. the dex_path either exists, or if not, its corresponding
+//   oat/vdex/art files where deleted successfully). In this case, out_secondary_dex_exists
+//   will be true if the secondary dex file still exists. If the secondary dex file does not exist,
+//   the method cleans up any previously generated compiler artifacts (oat, vdex, art).
+// Return false if there were errors during processing. In this case
+//   out_secondary_dex_exists will be set to false.
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+        const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+        const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+        /*out*/bool* out_secondary_dex_exists) {
+    // Set out to false to start with, just in case we have validation errors.
+    *out_secondary_dex_exists = false;
+    if (isas.size() == 0) {
+        LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
+        return false;
+    }
+
+    const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+    if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+            uid, storage_flag)) {
+        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+        return false;
+    }
+
+    if (access(dex_path.c_str(), F_OK) == 0) {
+        // The path exists, nothing to do. The odex files (if any) will be left untouched.
+        *out_secondary_dex_exists = true;
+        return true;
+    } else if (errno != ENOENT) {
+        PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+        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;
+        }
+
+        // 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(
+                multiuser_get_user_id(uid), dex_path, /*is_secondary*/true);
+        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;
+
+        // 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;
+    }
+
+    return result;
+}
+
+// Helper for move_ab, so that we can have common failure-case cleanup.
+static bool unlink_and_rename(const char* from, const char* to) {
+    // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
+    // return a failure.
+    struct stat s;
+    if (stat(to, &s) == 0) {
+        if (!S_ISREG(s.st_mode)) {
+            LOG(ERROR) << from << " is not a regular file to replace for A/B.";
+            return false;
+        }
+        if (unlink(to) != 0) {
+            LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
+            return false;
+        }
+    } else {
+        // This may be a permission problem. We could investigate the error code, but we'll just
+        // let the rename failure do the work for us.
+    }
+
+    // Try to rename "to" to "from."
+    if (rename(from, to) != 0) {
+        PLOG(ERROR) << "Could not rename " << from << " to " << to;
+        return false;
+    }
+    return true;
+}
+
+// Move/rename a B artifact (from) to an A artifact (to).
+static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
+    // Check whether B exists.
+    {
+        struct stat s;
+        if (stat(b_path.c_str(), &s) != 0) {
+            // Silently ignore for now. The service calling this isn't smart enough to understand
+            // lack of artifacts at the moment.
+            return false;
+        }
+        if (!S_ISREG(s.st_mode)) {
+            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+            // Try to unlink, but swallow errors.
+            unlink(b_path.c_str());
+            return false;
+        }
+    }
+
+    // Rename B to A.
+    if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
+        // Delete the b_path so we don't try again (or fail earlier).
+        if (unlink(b_path.c_str()) != 0) {
+            PLOG(ERROR) << "Could not unlink " << b_path;
+        }
+
+        return false;
+    }
+
+    return true;
+}
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+    // Get the current slot suffix. No suffix, no A/B.
+    std::string slot_suffix;
+    {
+        char buf[kPropertyValueMax];
+        if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
+            return false;
+        }
+        slot_suffix = buf;
+
+        if (!ValidateTargetSlotSuffix(slot_suffix)) {
+            LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
+            return false;
+        }
+    }
+
+    // Validate other inputs.
+    if (validate_apk_path(apk_path) != 0) {
+        LOG(ERROR) << "Invalid apk_path: " << apk_path;
+        return false;
+    }
+    if (validate_apk_path(oat_dir) != 0) {
+        LOG(ERROR) << "Invalid oat_dir: " << oat_dir;
+        return false;
+    }
+
+    char a_path[PKG_PATH_MAX];
+    if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
+        return false;
+    }
+    const std::string a_vdex_path = create_vdex_filename(a_path);
+    const std::string a_image_path = create_image_filename(a_path);
+
+    // B path = A path + slot suffix.
+    const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
+    const std::string b_vdex_path = StringPrintf("%s.%s", a_vdex_path.c_str(), slot_suffix.c_str());
+    const std::string b_image_path = StringPrintf("%s.%s",
+                                                  a_image_path.c_str(),
+                                                  slot_suffix.c_str());
+
+    bool success = true;
+    if (move_ab_path(b_path, a_path)) {
+        if (move_ab_path(b_vdex_path, a_vdex_path)) {
+            // Note: we can live without an app image. As such, ignore failure to move the image file.
+            //       If we decide to require the app image, or the app image being moved correctly,
+            //       then change accordingly.
+            constexpr bool kIgnoreAppImageFailure = true;
+
+            if (!a_image_path.empty()) {
+                if (!move_ab_path(b_image_path, a_image_path)) {
+                    unlink(a_image_path.c_str());
+                    if (!kIgnoreAppImageFailure) {
+                        success = false;
+                    }
+                }
+            }
+        } else {
+            // Cleanup: delete B image, ignore errors.
+            unlink(b_image_path.c_str());
+            success = false;
+        }
+    } else {
+        // Cleanup: delete B image, ignore errors.
+        unlink(b_vdex_path.c_str());
+        unlink(b_image_path.c_str());
+        success = false;
+    }
+    return success;
+}
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+    // Delete the oat/odex file.
+    char out_path[PKG_PATH_MAX];
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir,
+            /*is_secondary_dex*/false, out_path)) {
+        return false;
+    }
+
+    // In case of a permission failure report the issue. Otherwise just print a warning.
+    auto unlink_and_check = [](const char* path) -> bool {
+        int result = unlink(path);
+        if (result != 0) {
+            if (errno == EACCES || errno == EPERM) {
+                PLOG(ERROR) << "Could not unlink " << path;
+                return false;
+            }
+            PLOG(WARNING) << "Could not unlink " << path;
+        }
+        return true;
+    };
+
+    // Delete the oat/odex file.
+    bool return_value_oat = unlink_and_check(out_path);
+
+    // Derive and delete the app image.
+    bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
+
+    // Derive and delete the vdex file.
+    bool return_value_vdex = unlink_and_check(create_vdex_filename(out_path).c_str());
+
+    // Report success.
+    return return_value_oat && return_value_art && return_value_vdex;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
new file mode 100644
index 0000000..355adb1
--- /dev/null
+++ b/cmds/installd/dexopt.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEXOPT_H_
+#define DEXOPT_H_
+
+#include <sys/types.h>
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace installd {
+
+/* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int NO_DEXOPT_NEEDED            = 0;
+static constexpr int DEX2OAT_FROM_SCRATCH        = 1;
+static constexpr int DEX2OAT_FOR_BOOT_IMAGE      = 2;
+static constexpr int DEX2OAT_FOR_FILTER          = 3;
+static constexpr int DEX2OAT_FOR_RELOCATION      = 4;
+static constexpr int PATCHOAT_FOR_RELOCATION     = 5;
+
+// Clear the reference profile for the primary apk of the given package.
+bool clear_primary_reference_profile(const std::string& pkgname);
+// Clear the current profile for the primary apk of the given package and user.
+bool clear_primary_current_profile(const std::string& pkgname, userid_t user);
+// Clear all current profile for the primary apk of the given package.
+bool clear_primary_current_profiles(const std::string& pkgname);
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+
+// Decide if profile guided compilation is needed or not based on existing profiles.
+// The analysis is done for the primary apks (base + splits) of the given package.
+// Returns true if there is enough information in the current profiles that makes it
+// worth to recompile the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+bool analyze_primary_profiles(uid_t uid, const std::string& pkgname);
+
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
+
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+        const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+        const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+        /*out*/bool* out_secondary_dex_exists);
+
+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);
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // DEXOPT_H_
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index 93e1ce5..edcdb6a 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -14,19 +14,17 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "installd"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include <cutils/log.h>               // TODO: Move everything to base::logging.
+#include <log/log.h>              // TODO: Move everything to base::logging.
 
 #include <globals.h>
 #include <installd_constants.h>
 #include <utils.h>
 
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
 namespace android {
 namespace installd {
 
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 8f883db..35936a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -13,6 +13,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
+#define LOG_TAG "installd"
 
 #include <fcntl.h>
 #include <selinux/android.h>
@@ -20,30 +21,19 @@
 #include <sys/capability.h>
 #include <sys/fsuid.h>
 #include <sys/prctl.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 
 #include <android-base/logging.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>               // TODO: Move everything to base::logging.
 #include <cutils/properties.h>
-#include <cutils/sockets.h>
+#include <log/log.h>              // TODO: Move everything to base::logging.
 #include <private/android_filesystem_config.h>
 
-#include <commands.h>
-#include <globals.h>
-#include <installd_constants.h>
-#include <installd_deps.h>  // Need to fill in requirements of commands.
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-#define SOCKET_PATH "installd"
-
-#define BUFFER_MAX    1024  /* input buffer for commands */
-#define TOKEN_MAX     16    /* max number of arguments in buffer */
-#define REPLY_MAX     256   /* largest reply allowed */
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h"  // Need to fill in requirements of commands.
+#include "utils.h"
 
 namespace android {
 namespace installd {
@@ -65,17 +55,17 @@
                              const char *oat_dir,
                              const char *apk_path,
                              const char *instruction_set) {
-    char *file_name_start;
-    char *file_name_end;
+    const char *file_name_start;
+    const char *file_name_end;
 
     file_name_start = strrchr(apk_path, '/');
     if (file_name_start == NULL) {
-        ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
+        SLOGE("apk_path '%s' has no '/'s in it\n", apk_path);
         return false;
     }
     file_name_end = strrchr(apk_path, '.');
     if (file_name_end < file_name_start) {
-        ALOGE("apk_path '%s' has no extension\n", apk_path);
+        SLOGE("apk_path '%s' has no extension\n", apk_path);
         return false;
     }
 
@@ -101,14 +91,14 @@
                               const char *instruction_set) {
     if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set)
             + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) {
-        ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
+        SLOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
         return false;
     }
 
     strcpy(path, apk_path);
     char *end = strrchr(path, '/');
     if (end == NULL) {
-        ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
+        SLOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
         return false;
     }
     const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/');
@@ -118,7 +108,7 @@
     strcat(path, apk_end);         // path = /system/framework/oat/<isa>/whatever.jar\0
     end = strrchr(path, '.');
     if (end == NULL) {
-        ALOGE("apk_path '%s' has no extension.\n", apk_path);
+        SLOGE("apk_path '%s' has no extension.\n", apk_path);
         return false;
     }
     strcpy(end + 1, "odex");
@@ -151,12 +141,11 @@
         return false;
     }
 
-    sprintf(path,"%s%s/%s/%s%s",
+    sprintf(path,"%s%s/%s/%s",
             android_data_dir.path,
             DALVIK_CACHE,
             instruction_set,
-            src + 1, /* skip the leading / */
-            DALVIK_CACHE_POSTFIX);
+            src + 1 /* skip the leading / */);
 
     char* tmp =
             path +
@@ -171,407 +160,19 @@
         }
     }
 
+    strcat(path, DALVIK_CACHE_POSTFIX);
     return true;
 }
 
-
-static char* parse_null(char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
-static int do_ping(char **arg ATTRIBUTE_UNUSED, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return 0;
-}
-
-static int do_create_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags,
-            appid_t appid, const char* seinfo, int target_sdk_version */
-    return create_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]),
-                           atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_restorecon_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char* uuid, const char* pkgName, userid_t userid, int flags,
-            appid_t appid, const char* seinfo */
-    return restorecon_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atoi(arg[4]), arg[5]);
-}
-
-static int do_migrate_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags */
-    return migrate_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_clear_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
-    return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
-    return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-// We use otapreopt_chroot to get into the chroot.
-static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-
-static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
-                         char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // Time to fork and run otapreopt.
-
-    // Check that the tool exists.
-    struct stat s;
-    if (stat(kOtaPreopt, &s) != 0) {
-        LOG(ERROR) << "Otapreopt chroot tool not found.";
-        return -1;
-    }
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
-        argv[0] = kOtaPreopt;
-
-        for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
-            argv[i + 1] = args[i];
-        }
-
-        argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
-
-        execv(argv[0], (char * const *)argv);
-        PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
-        exit(99);
-    } else {
-        int res = wait_child(pid);
-        if (res == 0) {
-            ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
-        } else {
-            ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
-        }
-        return res;
-    }
-}
-
-static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
-                             char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    return dexopt(args);
-}
-
-using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
-                         char reply[REPLY_MAX]);
-
-static int do_dexopt(char **arg, char reply[REPLY_MAX])
-{
-    const char* args[DEXOPT_PARAM_COUNT];
-    for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
-        CHECK(arg[i] != nullptr);
-        args[i] = arg[i];
-    }
-
-    int dexopt_flags = atoi(arg[6]);
-    DexoptFn dexopt_fn;
-    if ((dexopt_flags & DEXOPT_OTA) != 0) {
-        dexopt_fn = do_ota_dexopt;
-    } else {
-        dexopt_fn = do_regular_dexopt;
-    }
-    return dexopt_fn(args, reply);
-}
-
-static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
-{
-    uid_t uid = static_cast<uid_t>(atoi(arg[0]));
-    const char* pkgname = arg[1];
-    if (merge_profiles(uid, pkgname)) {
-        strncpy(reply, "true", REPLY_MAX);
-    } else {
-        strncpy(reply, "false", REPLY_MAX);
-    }
-    return 0;
-}
-
-static int do_dump_profiles(char **arg, char reply[REPLY_MAX])
-{
-    uid_t uid = static_cast<uid_t>(atoi(arg[0]));
-    const char* pkgname = arg[1];
-    const char* dex_files = arg[2];
-    if (dump_profile(uid, pkgname, dex_files)) {
-        strncpy(reply, "true", REPLY_MAX);
-    } else {
-        strncpy(reply, "false", REPLY_MAX);
-    }
-    return 0;
-}
-
-static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return mark_boot_complete(arg[0] /* instruction set */);
-}
-
-static int do_rm_dex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */
-}
-
-static int do_free_cache(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) /* TODO int:free_size */
-{
-    return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1])); /* uuid, free_size */
-}
-
-static int do_get_app_size(char **arg, char reply[REPLY_MAX]) {
-    int64_t codesize = 0;
-    int64_t datasize = 0;
-    int64_t cachesize = 0;
-    int64_t asecsize = 0;
-    int res = 0;
-
-    /* const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-            const char* code_path */
-    res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]),
-            arg[5], &codesize, &datasize, &cachesize, &asecsize);
-
-    /*
-     * Each int64_t can take up 22 characters printed out. Make sure it
-     * doesn't go over REPLY_MAX in the future.
-     */
-    snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
-            codesize, datasize, cachesize, asecsize);
-    return res;
-}
-
-static int do_get_app_data_inode(char **arg, char reply[REPLY_MAX]) {
-    ino_t inode = 0;
-    int res = 0;
-
-    /* const char *uuid, const char *pkgname, int userid, int flags */
-    res = get_app_data_inode(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), &inode);
-
-    snprintf(reply, REPLY_MAX, "%" PRId64, (int64_t) inode);
-    return res;
-}
-
-static int do_move_complete_app(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char* from_uuid, const char *to_uuid, const char *package_name,
-            const char *data_app_name, appid_t appid, const char* seinfo,
-            int target_sdk_version */
-    return move_complete_app(parse_null(arg[0]), parse_null(arg[1]), arg[2], arg[3],
-                             atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_create_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* const char *uuid, userid_t userid, int user_serial, int flags */
-    return create_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_destroy_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* const char *uuid, userid_t userid, int flags */
-    return destroy_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]));
-}
-
-static int do_linklib(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return linklib(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
-}
-
-static int do_idmap(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return idmap(arg[0], arg[1], atoi(arg[2]));
-}
-
-static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* oat_dir, instruction_set */
-    return create_oat_dir(arg[0], arg[1]);
-}
-
-static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* oat_dir */
-    return rm_package_dir(arg[0]);
-}
-
-static int do_clear_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* package_name */
-    return clear_app_profiles(arg[0]);
-}
-
-static int do_destroy_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* package_name */
-    return destroy_app_profiles(arg[0]);
-}
-
-static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* relative_path, from_base, to_base */
-    return link_file(arg[0], arg[1], arg[2]);
-}
-
-static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // apk_path, instruction_set, oat_dir
-    return move_ab(arg[0], arg[1], arg[2]);
-}
-
-static int do_delete_odex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // apk_path, instruction_set, oat_dir
-    return delete_odex(arg[0], arg[1], arg[2]) ? 0 : -1;
-}
-
-struct cmdinfo {
-    const char *name;
-    unsigned numargs;
-    int (*func)(char **arg, char reply[REPLY_MAX]);
-};
-
-struct cmdinfo cmds[] = {
-    { "ping",                 0, do_ping },
-
-    { "create_app_data",      7, do_create_app_data },
-    { "restorecon_app_data",  6, do_restorecon_app_data },
-    { "migrate_app_data",     4, do_migrate_app_data },
-    { "clear_app_data",       5, do_clear_app_data },
-    { "destroy_app_data",     5, do_destroy_app_data },
-    { "move_complete_app",    7, do_move_complete_app },
-    { "get_app_size",         6, do_get_app_size },
-    { "get_app_data_inode",   4, do_get_app_data_inode },
-
-    { "create_user_data",     4, do_create_user_data },
-    { "destroy_user_data",    3, do_destroy_user_data },
-
-    { "dexopt",              10, do_dexopt },
-    { "markbootcomplete",     1, do_mark_boot_complete },
-    { "rmdex",                2, do_rm_dex },
-    { "freecache",            2, do_free_cache },
-    { "linklib",              4, do_linklib },
-    { "idmap",                3, do_idmap },
-    { "createoatdir",         2, do_create_oat_dir },
-    { "rmpackagedir",         1, do_rm_package_dir },
-    { "clear_app_profiles",   1, do_clear_app_profiles },
-    { "destroy_app_profiles", 1, do_destroy_app_profiles },
-    { "linkfile",             3, do_link_file },
-    { "move_ab",              3, do_move_ab },
-    { "merge_profiles",       2, do_merge_profiles },
-    { "dump_profiles",        3, do_dump_profiles },
-    { "delete_odex",          3, do_delete_odex },
-};
-
-static int readx(int s, void *_buf, int count)
-{
-    char *buf = (char *) _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = read(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            ALOGE("read error: %s\n", strerror(errno));
-            return -1;
-        }
-        if (r == 0) {
-            ALOGE("eof\n");
-            return -1; /* EOF */
-        }
-        n += r;
-    }
-    return 0;
-}
-
-static int writex(int s, const void *_buf, int count)
-{
-    const char *buf = (const char *) _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = write(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            ALOGE("write error: %s\n", strerror(errno));
-            return -1;
-        }
-        n += r;
-    }
-    return 0;
-}
-
-
-/* Tokenize the command buffer, locate a matching command,
- * ensure that the required number of arguments are provided,
- * call the function(), return the result.
- */
-static int execute(int s, char cmd[BUFFER_MAX])
-{
-    char reply[REPLY_MAX];
-    char *arg[TOKEN_MAX+1];
-    unsigned i;
-    unsigned n = 0;
-    unsigned short count;
-    int ret = -1;
-
-    // ALOGI("execute('%s')\n", cmd);
-
-        /* default reply is "" */
-    reply[0] = 0;
-
-        /* n is number of args (not counting arg[0]) */
-    arg[0] = cmd;
-    while (*cmd) {
-        if (isspace(*cmd)) {
-            *cmd++ = 0;
-            n++;
-            arg[n] = cmd;
-            if (n == TOKEN_MAX) {
-                ALOGE("too many arguments\n");
-                goto done;
-            }
-        }
-        if (*cmd) {
-          cmd++;
-        }
-    }
-
-    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
-        if (!strcmp(cmds[i].name,arg[0])) {
-            if (n != cmds[i].numargs) {
-                ALOGE("%s requires %d arguments (%d given)\n",
-                     cmds[i].name, cmds[i].numargs, n);
-            } else {
-                ret = cmds[i].func(arg + 1, reply);
-            }
-            goto done;
-        }
-    }
-    ALOGE("unsupported command '%s'\n", arg[0]);
-
-done:
-    if (reply[0]) {
-        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
-    } else {
-        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
-    }
-    if (n > BUFFER_MAX) n = BUFFER_MAX;
-    count = n;
-
-    // ALOGI("reply: '%s'\n", cmd);
-    if (writex(s, &count, sizeof(count))) return -1;
-    if (writex(s, cmd, count)) return -1;
-    return 0;
-}
-
 static bool initialize_globals() {
     const char* data_path = getenv("ANDROID_DATA");
     if (data_path == nullptr) {
-        ALOGE("Could not find ANDROID_DATA");
+        SLOGE("Could not find ANDROID_DATA");
         return false;
     }
     const char* root_path = getenv("ANDROID_ROOT");
     if (root_path == nullptr) {
-        ALOGE("Could not find ANDROID_ROOT");
+        SLOGE("Could not find ANDROID_ROOT");
         return false;
     }
 
@@ -597,12 +198,12 @@
     }
 
     if (ensure_config_user_dirs(0) == -1) {
-        ALOGE("Failed to setup misc for user 0");
+        SLOGE("Failed to setup misc for user 0");
         goto fail;
     }
 
     if (version == 2) {
-        ALOGD("Upgrading to /data/misc/user directories");
+        SLOGD("Upgrading to /data/misc/user directories");
 
         char misc_dir[PATH_MAX];
         snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path);
@@ -643,12 +244,12 @@
                 gid_t gid = uid;
                 if (access(keychain_added_dir, F_OK) == 0) {
                     if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) {
-                        ALOGE("Some files failed to copy");
+                        SLOGE("Some files failed to copy");
                     }
                 }
                 if (access(keychain_removed_dir, F_OK) == 0) {
                     if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) {
-                        ALOGE("Some files failed to copy");
+                        SLOGE("Some files failed to copy");
                     }
                 }
             }
@@ -668,7 +269,7 @@
     // Persist layout version if changed
     if (version != oldVersion) {
         if (fs_write_atomic_int(version_path, version) == -1) {
-            ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+            SLOGE("Failed to save version to %s: %s", version_path, strerror(errno));
             goto fail;
         }
     }
@@ -702,80 +303,41 @@
 }
 
 static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
-    char buf[BUFFER_MAX];
-    struct sockaddr addr;
-    socklen_t alen;
-    int lsocket, s;
+    int ret;
     int selinux_enabled = (is_selinux_enabled() > 0);
 
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     android::base::InitLogging(argv);
 
-    ALOGI("installd firing up\n");
+    SLOGI("installd firing up");
 
     union selinux_callback cb;
     cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
     if (!initialize_globals()) {
-        ALOGE("Could not initialize globals; exiting.\n");
+        SLOGE("Could not initialize globals; exiting.\n");
         exit(1);
     }
 
     if (initialize_directories() < 0) {
-        ALOGE("Could not create directories; exiting.\n");
+        SLOGE("Could not create directories; exiting.\n");
         exit(1);
     }
 
     if (selinux_enabled && selinux_status_open(true) < 0) {
-        ALOGE("Could not open selinux status; exiting.\n");
+        SLOGE("Could not open selinux status; exiting.\n");
         exit(1);
     }
 
-    lsocket = android_get_control_socket(SOCKET_PATH);
-    if (lsocket < 0) {
-        ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
+    if ((ret = InstalldNativeService::start()) != android::OK) {
+        SLOGE("Unable to start InstalldNativeService: %d", ret);
         exit(1);
     }
-    if (listen(lsocket, 5)) {
-        ALOGE("Listen on socket failed: %s\n", strerror(errno));
-        exit(1);
-    }
-    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
 
-    for (;;) {
-        alen = sizeof(addr);
-        s = accept(lsocket, &addr, &alen);
-        if (s < 0) {
-            ALOGE("Accept failed: %s\n", strerror(errno));
-            continue;
-        }
-        fcntl(s, F_SETFD, FD_CLOEXEC);
+    IPCThreadState::self()->joinThreadPool();
 
-        ALOGI("new connection\n");
-        for (;;) {
-            unsigned short count;
-            if (readx(s, &count, sizeof(count))) {
-                ALOGE("failed to read size\n");
-                break;
-            }
-            if ((count < 1) || (count >= BUFFER_MAX)) {
-                ALOGE("invalid size %d\n", count);
-                break;
-            }
-            if (readx(s, buf, count)) {
-                ALOGE("failed to read command\n");
-                break;
-            }
-            buf[count] = 0;
-            if (selinux_enabled && selinux_status_updated() > 0) {
-                selinux_android_seapp_context_reload();
-            }
-            if (execute(s, buf)) break;
-        }
-        ALOGI("closing connection\n");
-        close(s);
-    }
+    LOG(INFO) << "installd shutting down";
 
     return 0;
 }
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 5e4c925..240aa49 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,3 +1,103 @@
+
 service installd /system/bin/installd
     class main
-    socket installd stream 600 system system
+
+on early-boot
+    mkdir /config/sdcardfs/extensions/1055
+    mkdir /config/sdcardfs/extensions/1056
+    mkdir /config/sdcardfs/extensions/1057
+    mkdir /config/sdcardfs/extensions/1056/3gpp
+    mkdir /config/sdcardfs/extensions/1056/3gp
+    mkdir /config/sdcardfs/extensions/1056/3gpp2
+    mkdir /config/sdcardfs/extensions/1056/3g2
+    mkdir /config/sdcardfs/extensions/1056/avi
+    mkdir /config/sdcardfs/extensions/1056/dl
+    mkdir /config/sdcardfs/extensions/1056/dif
+    mkdir /config/sdcardfs/extensions/1056/dv
+    mkdir /config/sdcardfs/extensions/1056/fli
+    mkdir /config/sdcardfs/extensions/1056/m4v
+    mkdir /config/sdcardfs/extensions/1056/ts
+    mkdir /config/sdcardfs/extensions/1056/mpeg
+    mkdir /config/sdcardfs/extensions/1056/mpg
+    mkdir /config/sdcardfs/extensions/1056/mpe
+    mkdir /config/sdcardfs/extensions/1056/mp4
+    mkdir /config/sdcardfs/extensions/1056/vob
+    mkdir /config/sdcardfs/extensions/1056/qt
+    mkdir /config/sdcardfs/extensions/1056/mov
+    mkdir /config/sdcardfs/extensions/1056/mxu
+    mkdir /config/sdcardfs/extensions/1056/webm
+    mkdir /config/sdcardfs/extensions/1056/lsf
+    mkdir /config/sdcardfs/extensions/1056/lsx
+    mkdir /config/sdcardfs/extensions/1056/mkv
+    mkdir /config/sdcardfs/extensions/1056/mng
+    mkdir /config/sdcardfs/extensions/1056/asf
+    mkdir /config/sdcardfs/extensions/1056/asx
+    mkdir /config/sdcardfs/extensions/1056/wm
+    mkdir /config/sdcardfs/extensions/1056/wmv
+    mkdir /config/sdcardfs/extensions/1056/wmx
+    mkdir /config/sdcardfs/extensions/1056/wvx
+    mkdir /config/sdcardfs/extensions/1056/movie
+    mkdir /config/sdcardfs/extensions/1056/wrf
+    mkdir /config/sdcardfs/extensions/1057/bmp
+    mkdir /config/sdcardfs/extensions/1057/gif
+    mkdir /config/sdcardfs/extensions/1057/jpg
+    mkdir /config/sdcardfs/extensions/1057/jpeg
+    mkdir /config/sdcardfs/extensions/1057/jpe
+    mkdir /config/sdcardfs/extensions/1057/pcx
+    mkdir /config/sdcardfs/extensions/1057/png
+    mkdir /config/sdcardfs/extensions/1057/svg
+    mkdir /config/sdcardfs/extensions/1057/svgz
+    mkdir /config/sdcardfs/extensions/1057/tiff
+    mkdir /config/sdcardfs/extensions/1057/tif
+    mkdir /config/sdcardfs/extensions/1057/wbmp
+    mkdir /config/sdcardfs/extensions/1057/webp
+    mkdir /config/sdcardfs/extensions/1057/dng
+    mkdir /config/sdcardfs/extensions/1057/cr2
+    mkdir /config/sdcardfs/extensions/1057/ras
+    mkdir /config/sdcardfs/extensions/1057/art
+    mkdir /config/sdcardfs/extensions/1057/jng
+    mkdir /config/sdcardfs/extensions/1057/nef
+    mkdir /config/sdcardfs/extensions/1057/nrw
+    mkdir /config/sdcardfs/extensions/1057/orf
+    mkdir /config/sdcardfs/extensions/1057/rw2
+    mkdir /config/sdcardfs/extensions/1057/pef
+    mkdir /config/sdcardfs/extensions/1057/psd
+    mkdir /config/sdcardfs/extensions/1057/pnm
+    mkdir /config/sdcardfs/extensions/1057/pbm
+    mkdir /config/sdcardfs/extensions/1057/pgm
+    mkdir /config/sdcardfs/extensions/1057/ppm
+    mkdir /config/sdcardfs/extensions/1057/srw
+    mkdir /config/sdcardfs/extensions/1057/arw
+    mkdir /config/sdcardfs/extensions/1057/rgb
+    mkdir /config/sdcardfs/extensions/1057/xbm
+    mkdir /config/sdcardfs/extensions/1057/xpm
+    mkdir /config/sdcardfs/extensions/1057/xwd
+    mkdir /config/sdcardfs/extensions/1055/aac
+    mkdir /config/sdcardfs/extensions/1055/aac
+    mkdir /config/sdcardfs/extensions/1055/amr
+    mkdir /config/sdcardfs/extensions/1055/awb
+    mkdir /config/sdcardfs/extensions/1055/snd
+    mkdir /config/sdcardfs/extensions/1055/flac
+    mkdir /config/sdcardfs/extensions/1055/flac
+    mkdir /config/sdcardfs/extensions/1055/mp3
+    mkdir /config/sdcardfs/extensions/1055/mpga
+    mkdir /config/sdcardfs/extensions/1055/mpega
+    mkdir /config/sdcardfs/extensions/1055/mp2
+    mkdir /config/sdcardfs/extensions/1055/m4a
+    mkdir /config/sdcardfs/extensions/1055/aif
+    mkdir /config/sdcardfs/extensions/1055/aiff
+    mkdir /config/sdcardfs/extensions/1055/aifc
+    mkdir /config/sdcardfs/extensions/1055/gsm
+    mkdir /config/sdcardfs/extensions/1055/mka
+    mkdir /config/sdcardfs/extensions/1055/m3u
+    mkdir /config/sdcardfs/extensions/1055/wma
+    mkdir /config/sdcardfs/extensions/1055/wax
+    mkdir /config/sdcardfs/extensions/1055/ra
+    mkdir /config/sdcardfs/extensions/1055/rm
+    mkdir /config/sdcardfs/extensions/1055/ram
+    mkdir /config/sdcardfs/extensions/1055/ra
+    mkdir /config/sdcardfs/extensions/1055/pls
+    mkdir /config/sdcardfs/extensions/1055/sd2
+    mkdir /config/sdcardfs/extensions/1055/wav
+    mkdir /config/sdcardfs/extensions/1055/ogg
+    mkdir /config/sdcardfs/extensions/1055/oga
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 41732cc..6a81cfc 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -28,8 +28,7 @@
 
 // This is used as a string literal, can't be constants. TODO: std::string...
 #define DALVIK_CACHE "dalvik-cache"
-constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex";
-constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex";
+constexpr const char* DALVIK_CACHE_POSTFIX = "@classes.dex";
 
 constexpr size_t PKG_NAME_MAX = 128u;   /* largest allowed package name */
 constexpr size_t PKG_PATH_MAX = 256u;   /* max size of any path we use */
@@ -39,20 +38,32 @@
  * frameworks/base/services/core/java/com/android/server/pm/Installer.java
  ***************************************************************************/
 constexpr int DEXOPT_PUBLIC         = 1 << 1;
-constexpr int DEXOPT_SAFEMODE       = 1 << 2;
-constexpr int DEXOPT_DEBUGGABLE     = 1 << 3;
-constexpr int DEXOPT_BOOTCOMPLETE   = 1 << 4;
-constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
-constexpr int DEXOPT_OTA            = 1 << 6;
+constexpr int DEXOPT_DEBUGGABLE     = 1 << 2;
+constexpr int DEXOPT_BOOTCOMPLETE   = 1 << 3;
+constexpr int DEXOPT_PROFILE_GUIDED = 1 << 4;
+constexpr int DEXOPT_SECONDARY_DEX  = 1 << 5;
+// DEXOPT_FORCE, DEXOPT_STORAGE_CE, DEXOPT_STORAGE_DE are exposed for secondary
+// dex files only. Primary apks are analyzed in PackageManager and installd
+// does not need to know if the compilation is forced or on what kind of storage
+// the dex files are.
+constexpr int DEXOPT_FORCE          = 1 << 6;
+constexpr int DEXOPT_STORAGE_CE     = 1 << 7;
+constexpr int DEXOPT_STORAGE_DE     = 1 << 8;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
     DEXOPT_PUBLIC
-    | DEXOPT_SAFEMODE
     | DEXOPT_DEBUGGABLE
     | DEXOPT_BOOTCOMPLETE
     | DEXOPT_PROFILE_GUIDED
-    | DEXOPT_OTA;
+    | DEXOPT_SECONDARY_DEX
+    | DEXOPT_FORCE
+    | DEXOPT_STORAGE_CE
+    | DEXOPT_STORAGE_DE;
+
+// NOTE: keep in sync with StorageManager
+constexpr int FLAG_STORAGE_DE = 1 << 0;
+constexpr int FLAG_STORAGE_CE = 1 << 1;
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
new file mode 100644
index 0000000..131487d
--- /dev/null
+++ b/cmds/installd/matchgen.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+# 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.
+
+import collections, sys
+
+TYPES = {
+    "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"],
+    "AID_MEDIA_VIDEO": ["3gpp","3gp","3gpp2","3g2","avi","dl","dif","dv","fli","m4v","ts","mpeg","mpg","mpe","mp4","vob","qt","mov","mxu","webm","lsf","lsx","mkv","mng","asf","asx","wm","wmv","wmx","wvx","movie","wrf"],
+    "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"]
+}
+
+if "--rc" in sys.argv:
+    print "on early-boot"
+    print "    mkdir /config/sdcardfs/extensions/1055"
+    print "    mkdir /config/sdcardfs/extensions/1056"
+    print "    mkdir /config/sdcardfs/extensions/1057"
+    for gid, exts in TYPES.iteritems():
+        if gid is "AID_MEDIA_AUDIO": gid = "1055"
+        if gid is "AID_MEDIA_VIDEO": gid = "1056"
+        if gid is "AID_MEDIA_IMAGE": gid = "1057"
+        for ext in exts:
+            print "    mkdir /config/sdcardfs/extensions/%s/%s" % (gid, ext)
+    exit()
+
+print """/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+"""
+
+trie = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: ""))))))
+
+for t in TYPES:
+    for v in TYPES[t]:
+        v = v.lower()
+        target = trie
+        for c in v:
+            target = target[c]
+        target["\0"] = t
+
+def dump(target, index):
+    prefix = "    " * (index + 1)
+    print "%sswitch (ext[%d]) {" % (prefix, index)
+    for k in sorted(target.keys()):
+        if k == "\0":
+            print "%scase '\\0': return %s;" % (prefix, target[k])
+        else:
+            upper = k.upper()
+            if k != upper:
+                print "%scase '%s': case '%s':" % (prefix, k, upper)
+            else:
+                print "%scase '%s':" % (prefix, k)
+            dump(target[k], index + 1)
+    print "%s}" % (prefix)
+
+dump(trie, 0)
+
+print """
+    return 0;
+}
+"""
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 5fa972a..68cb0d7 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -16,6 +16,7 @@
 
 #include <algorithm>
 #include <inttypes.h>
+#include <limits>
 #include <random>
 #include <regex>
 #include <selinux/android.h>
@@ -32,17 +33,19 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <dex2oat_return_codes.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
-#include <commands.h>
-#include <file_parsing.h>
-#include <globals.h>
-#include <installd_deps.h>  // Need to fill in requirements of commands.
-#include <otapreopt_utils.h>
-#include <system_properties.h>
-#include <utils.h>
+#include "dexopt.h"
+#include "file_parsing.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h"  // Need to fill in requirements of commands.
+#include "otapreopt_utils.h"
+#include "system_properties.h"
+#include "utils.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
@@ -61,6 +64,25 @@
 namespace android {
 namespace installd {
 
+// Check expected values for dexopt flags. If you need to change this:
+//
+//   RUN AN A/B OTA TO MAKE SURE THINGS STILL WORK!
+//
+// You most likely need to increase the protocol version and all that entails!
+
+static_assert(DEXOPT_PUBLIC         == 1 << 1, "DEXOPT_PUBLIC unexpected.");
+static_assert(DEXOPT_DEBUGGABLE     == 1 << 2, "DEXOPT_DEBUGGABLE unexpected.");
+static_assert(DEXOPT_BOOTCOMPLETE   == 1 << 3, "DEXOPT_BOOTCOMPLETE unexpected.");
+static_assert(DEXOPT_PROFILE_GUIDED == 1 << 4, "DEXOPT_PROFILE_GUIDED unexpected.");
+static_assert(DEXOPT_SECONDARY_DEX  == 1 << 5, "DEXOPT_SECONDARY_DEX unexpected.");
+static_assert(DEXOPT_FORCE          == 1 << 6, "DEXOPT_FORCE unexpected.");
+static_assert(DEXOPT_STORAGE_CE     == 1 << 7, "DEXOPT_STORAGE_CE unexpected.");
+static_assert(DEXOPT_STORAGE_DE     == 1 << 8, "DEXOPT_STORAGE_DE unexpected.");
+
+static_assert(DEXOPT_MASK           == 0x1fe, "DEXOPT_MASK unexpected.");
+
+
+
 template<typename T>
 static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
     return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
@@ -144,6 +166,20 @@
 
 private:
 
+    struct Parameters {
+        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;
+    };
+
     bool ReadSystemProperties() {
         static constexpr const char* kPropertyFiles[] = {
                 "/default.prop", "/system/build.prop"
@@ -245,15 +281,23 @@
         return true;
     }
 
-    bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) {
-        // Expected command line:
-        //   target-slot dexopt {DEXOPT_PARAMETERS}
-        // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT
-        // of them. We store them in package_parameters_ (size checks are done when
-        // parsing the special parameters and when copying into package_parameters_.
+    bool ParseUInt(const char* in, uint32_t* out) {
+        char* end;
+        long long int result = strtoll(in, &end, 0);
+        if (in == end || *end != '\0') {
+            return false;
+        }
+        if (result < std::numeric_limits<uint32_t>::min() ||
+                std::numeric_limits<uint32_t>::max() < result) {
+            return false;
+        }
+        *out = static_cast<uint32_t>(result);
+        return true;
+    }
 
-        static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_),
-                      "Unexpected dexopt param count");
+    bool ReadArguments(int argc, char** argv) {
+        // Expected command line:
+        //   target-slot [version] dexopt {DEXOPT_PARAMETERS}
 
         const char* target_slot_arg = argv[1];
         if (target_slot_arg == nullptr) {
@@ -267,28 +311,229 @@
             return false;
         }
 
-        // Check for "dexopt" next.
+        // Check for version or "dexopt" next.
+        if (argv[2] == nullptr) {
+            LOG(ERROR) << "Missing parameters";
+            return false;
+        }
+
+        if (std::string("dexopt").compare(argv[2]) == 0) {
+            // This is version 1 (N) or pre-versioning version 2.
+            constexpr int kV2ArgCount =   1   // "otapreopt"
+                                        + 1   // slot
+                                        + 1   // "dexopt"
+                                        + 1   // apk_path
+                                        + 1   // uid
+                                        + 1   // pkg
+                                        + 1   // isa
+                                        + 1   // dexopt_needed
+                                        + 1   // oat_dir
+                                        + 1   // dexopt_flags
+                                        + 1   // filter
+                                        + 1   // volume
+                                        + 1   // libs
+                                        + 1;  // seinfo
+            if (argc == kV2ArgCount) {
+                return ReadArgumentsV2(argc, argv, false);
+            } else {
+                return ReadArgumentsV1(argc, argv);
+            }
+        }
+
+        uint32_t version;
+        if (!ParseUInt(argv[2], &version)) {
+            LOG(ERROR) << "Could not parse version: " << argv[2];
+            return false;
+        }
+
+        switch (version) {
+            case 2:
+                return ReadArgumentsV2(argc, argv, true);
+
+            default:
+                LOG(ERROR) << "Unsupported version " << version;
+                return false;
+        }
+    }
+
+    bool ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, char** argv, bool versioned) {
+        size_t dexopt_index = versioned ? 3 : 2;
+
+        // 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;
+
+                default:
+                    LOG(ERROR) << "Too many arguments, got " << param;
+                    return false;
+            }
+        }
+
+        if (param_index != 11) {
+            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;
+    }
+
+    bool ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, char** argv) {
+        // Check for "dexopt".
         if (argv[2] == nullptr) {
             LOG(ERROR) << "Missing parameters";
             return false;
         }
         if (std::string("dexopt").compare(argv[2]) != 0) {
-            LOG(ERROR) << "Second parameter not dexopt: " << argv[2];
+            LOG(ERROR) << "Expected \"dexopt\"";
             return false;
         }
 
-        // Copy the rest into package_parameters_, but be careful about over- and underflow.
-        size_t index = 0;
-        while (index < DEXOPT_PARAM_COUNT &&
-                argv[index + 3] != nullptr) {
-            package_parameters_[index] = argv[index + 3];
-            index++;
+        size_t param_index = 0;
+        for (;; ++param_index) {
+            const char* param = argv[3 + 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: {
+                    // Version 1 had:
+                    //   DEXOPT_DEX2OAT_NEEDED       = 1
+                    //   DEXOPT_PATCHOAT_NEEDED      = 2
+                    //   DEXOPT_SELF_PATCHOAT_NEEDED = 3
+                    // We will simply use DEX2OAT_FROM_SCRATCH.
+                    package_parameters_.dexopt_needed = DEX2OAT_FROM_SCRATCH;
+                    break;
+                }
+
+                case 5:
+                    package_parameters_.oat_dir = param;
+                    break;
+
+                case 6: {
+                    // Version 1 had:
+                    constexpr int OLD_DEXOPT_PUBLIC         = 1 << 1;
+                    // Note: DEXOPT_SAFEMODE has been removed.
+                    // constexpr int OLD_DEXOPT_SAFEMODE       = 1 << 2;
+                    constexpr int OLD_DEXOPT_DEBUGGABLE     = 1 << 3;
+                    constexpr int OLD_DEXOPT_BOOTCOMPLETE   = 1 << 4;
+                    constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5;
+                    constexpr int OLD_DEXOPT_OTA            = 1 << 6;
+                    int input = atoi(param);
+                    package_parameters_.dexopt_flags =
+                            ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) |
+                            ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) |
+                            ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) |
+                            ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) |
+                            ReplaceMask(input, OLD_DEXOPT_OTA, 0);
+                    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;
+
+                default:
+                    LOG(ERROR) << "Too many arguments, got " << param;
+                    return false;
+            }
         }
-        if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) {
-            LOG(ERROR) << "Wrong number of parameters";
+
+        if (param_index != 10) {
+            LOG(ERROR) << "Not enough parameters";
             return false;
         }
 
+        // Set se_info to null. It is only relevant for secondary dex files, which we won't
+        // receive from a v1 A side.
+        package_parameters_.se_info = nullptr;
+
         return true;
     }
 
@@ -305,11 +550,11 @@
     // Ensure that we have the right boot image. The first time any app is
     // compiled, we'll try to generate it.
     bool PrepareBootImage(bool force) const {
-        if (package_parameters_[kISAIndex] == nullptr) {
+        if (package_parameters_.instruction_set == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
         }
-        const char* isa = package_parameters_[kISAIndex];
+        const char* isa = package_parameters_.instruction_set;
 
         // Check whether the file exists where expected.
         std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
@@ -508,6 +753,10 @@
     }
 
     static const char* ParseNull(const char* arg) {
+        // b/38186355. Revert soon.
+        if (strcmp(arg, "!null") == 0) {
+            return nullptr;
+        }
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
 
@@ -535,14 +784,12 @@
         //       (This is ugly as it's the only thing where we need to understand the contents
         //        of package_parameters_, but it beats postponing the decision or using the call-
         //        backs to do weird things.)
-        constexpr size_t kApkPathIndex = 0;
-        CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex);
-        CHECK(package_parameters_[kApkPathIndex] != nullptr);
-        if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) {
-            const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/');
+        const char* apk_path = package_parameters_.apk_path;
+        CHECK(apk_path != nullptr);
+        if (StartsWith(apk_path, android_root_.c_str())) {
+            const char* last_slash = strrchr(apk_path, '/');
             if (last_slash != nullptr) {
-                std::string path(package_parameters_[kApkPathIndex],
-                                 last_slash - package_parameters_[kApkPathIndex] + 1);
+                std::string path(apk_path, last_slash - apk_path + 1);
                 CHECK(EndsWith(path, "/"));
                 path = path + "oat";
                 if (access(path.c_str(), F_OK) == 0) {
@@ -556,36 +803,64 @@
         // partition will not be available and fail to build. This is problematic, as
         // this tool will wipe the OTA artifact cache and try again (for robustness after
         // a failed OTA with remaining cache artifacts).
-        if (access(package_parameters_[kApkPathIndex], F_OK) != 0) {
-            LOG(WARNING) << "Skipping preopt of non-existing package "
-                         << package_parameters_[kApkPathIndex];
+        if (access(apk_path, F_OK) != 0) {
+            LOG(WARNING) << "Skipping preopt of non-existing package " << apk_path;
             return true;
         }
 
         return false;
     }
 
+    // Run dexopt with the parameters of package_parameters_.
+    int Dexopt() {
+        return dexopt(package_parameters_.apk_path,
+                      package_parameters_.uid,
+                      package_parameters_.pkgName,
+                      package_parameters_.instruction_set,
+                      package_parameters_.dexopt_needed,
+                      package_parameters_.oat_dir,
+                      package_parameters_.dexopt_flags,
+                      package_parameters_.compiler_filter,
+                      package_parameters_.volume_uuid,
+                      package_parameters_.shared_libraries,
+                      package_parameters_.se_info);
+    }
+
     int RunPreopt() {
         if (ShouldSkipPreopt()) {
             return 0;
         }
 
-        int dexopt_result = dexopt(package_parameters_);
+        int dexopt_result = Dexopt();
         if (dexopt_result == 0) {
             return 0;
         }
 
         // If the dexopt failed, we may have a stale boot image from a previous OTA run.
-        // Try to delete and retry.
+        // Then regenerate and retry.
+        if (WEXITSTATUS(dexopt_result) ==
+                static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime)) {
+            if (!PrepareBootImage(/* force */ true)) {
+                LOG(ERROR) << "Forced boot image creating failed. Original error return was "
+                        << dexopt_result;
+                return dexopt_result;
+            }
 
-        if (!PrepareBootImage(/* force */ true)) {
-            LOG(ERROR) << "Forced boot image creating failed. Original error return was "
-                         << dexopt_result;
+            int dexopt_result_boot_image_retry = Dexopt();
+            if (dexopt_result_boot_image_retry == 0) {
+                return 0;
+            }
+        }
+
+        // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
+        // if possible.
+        if ((package_parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
             return dexopt_result;
         }
 
-        LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated.";
-        return dexopt(package_parameters_);
+        LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation";
+        package_parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED;
+        return Dexopt();
     }
 
     ////////////////////////////////////
@@ -715,7 +990,7 @@
     std::string boot_classpath_;
     std::string asec_mountpoint_;
 
-    const char* package_parameters_[DEXOPT_PARAM_COUNT];
+    Parameters package_parameters_;
 
     // Store environment values we need to set.
     std::vector<std::string> environ_;
@@ -823,7 +1098,7 @@
                                               DALVIK_CACHE,
                                               instruction_set,
                                               from_src.c_str(),
-                                              DALVIK_CACHE_POSTFIX2);
+                                              DALVIK_CACHE_POSTFIX);
 
     if (assembled_path.length() + 1 > PKG_PATH_MAX) {
         return false;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 5ea89e6..2030997 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -25,8 +25,8 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
-#include <commands.h>
-#include <otapreopt_utils.h>
+#include "installd_constants.h"
+#include "otapreopt_utils.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
@@ -136,44 +136,18 @@
 
     // Now go on and run otapreopt.
 
-    // Incoming:  cmd + status-fd + target-slot + "dexopt" + dexopt-params + null
-    // Outgoing:  cmd             + target-slot + "dexopt" + dexopt-params + null
-    constexpr size_t kInArguments =   1                       // Binary name.
-                                    + 1                       // status file descriptor.
-                                    + 1                       // target-slot.
-                                    + 1                       // "dexopt."
-                                    + DEXOPT_PARAM_COUNT      // dexopt parameters.
-                                    + 1;                      // null termination.
-    constexpr size_t kOutArguments =   1                       // Binary name.
-                                     + 1                       // target-slot.
-                                     + 1                       // "dexopt."
-                                     + DEXOPT_PARAM_COUNT      // dexopt parameters.
-                                     + 1;                      // null termination.
-    const char* argv[kOutArguments];
-    if (static_cast<size_t>(argc) !=  kInArguments - 1 /* null termination */) {
-        LOG(ERROR) << "Unexpected argument size "
-                   << argc
-                   << " vs "
-                   << (kInArguments - 1);
-        for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
-            if (arg[i] == nullptr) {
-                LOG(ERROR) << "(null)";
-            } else {
-                LOG(ERROR) << "\"" << arg[i] << "\"";
-            }
-        }
-        exit(206);
-    }
+    // Incoming:  cmd + status-fd + target-slot + cmd... + null      | Incoming | = argc + 1
+    // Outgoing:  cmd             + target-slot + cmd... + null      | Outgoing | = argc
+    const char** argv = new const char*[argc];
+
     argv[0] = "/system/bin/otapreopt";
 
     // The first parameter is the status file descriptor, skip.
-
-    for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) {
-        argv[i] = arg[i + 1];
+    for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) {
+        argv[i - 1] = arg[i];
     }
-    argv[kOutArguments - 1] = nullptr;
 
-    execv(argv[0], (char * const *)argv);
+    execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv)));
     PLOG(ERROR) << "execv(OTAPREOPT) failed.";
     exit(99);
 }
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
new file mode 100644
index 0000000..630c1f3
--- /dev/null
+++ b/cmds/installd/tests/Android.bp
@@ -0,0 +1,54 @@
+// Build the unit tests for installd
+cc_test {
+    name: "installd_utils_test",
+    clang: true,
+    srcs: ["installd_utils_test.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+        "libcutils",
+    ],
+    static_libs: [
+        "libinstalld",
+        "libdiskusage",
+    ],
+}
+
+cc_test {
+    name: "installd_cache_test",
+    clang: true,
+    srcs: ["installd_cache_test.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "liblogwrap",
+        "libselinux",
+        "libutils",
+    ],
+    static_libs: [
+        "libinstalld",
+        "libdiskusage",
+    ],
+}
+
+cc_test {
+    name: "installd_service_test",
+    clang: true,
+    srcs: ["installd_service_test.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "liblogwrap",
+        "libselinux",
+        "libutils",
+    ],
+    static_libs: [
+        "libinstalld",
+        "libdiskusage",
+    ],
+}
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
deleted file mode 100644
index 38a9f69..0000000
--- a/cmds/installd/tests/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Build the unit tests for installd
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-# Build the unit tests.
-test_src_files := \
-    installd_utils_test.cpp
-
-shared_libraries := \
-    libbase \
-    libutils \
-    libcutils \
-
-static_libraries := \
-    libinstalld \
-    libdiskusage \
-
-c_includes := \
-    frameworks/native/cmds/installd
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval LOCAL_CLANG := true) \
-    $(eval include $(BUILD_NATIVE_TEST)) \
-)
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
new file mode 100644
index 0000000..aed068c
--- /dev/null
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+constexpr const char* kTestUuid = "TEST";
+
+constexpr int64_t kKbInBytes = 1024;
+constexpr int64_t kMbInBytes = 1024 * kKbInBytes;
+constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
+constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
+
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+
+int get_property(const char *key, char *value, const char *default_value) {
+    return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *oat_dir ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *src ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+static void mkdir(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    ::mkdir(fullPath, 0755);
+}
+
+static void touch(const char* path, int len, int time) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644);
+    ::fallocate(fd, 0, 0, len);
+    ::close(fd);
+    struct utimbuf times;
+    times.actime = times.modtime = std::time(0) + time;
+    ::utime(fullPath, &times);
+}
+
+static int exists(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    return ::access(fullPath, F_OK);
+}
+
+static int64_t size(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    struct stat buf;
+    if (!stat(fullPath, &buf)) {
+        return buf.st_size;
+    } else {
+        return -1;
+    }
+}
+
+static int64_t free() {
+    struct statvfs buf;
+    if (!statvfs("/data/local/tmp", &buf)) {
+        return buf.f_bavail * buf.f_frsize;
+    } else {
+        PLOG(ERROR) << "Failed to statvfs";
+        return -1;
+    }
+}
+
+static void setxattr(const char* path, const char* key) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    ::setxattr(fullPath, key, "", 0, 0);
+}
+
+class CacheTest : public testing::Test {
+protected:
+    InstalldNativeService* service;
+    std::unique_ptr<std::string> testUuid;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service = new InstalldNativeService();
+        testUuid = std::make_unique<std::string>();
+        *testUuid = std::string(kTestUuid);
+        system("mkdir -p /data/local/tmp/user/0");
+    }
+
+    virtual void TearDown() {
+        delete service;
+        system("rm -rf /data/local/tmp/user");
+    }
+};
+
+TEST_F(CacheTest, FreeCache_All) {
+    LOG(INFO) << "FreeCache_All";
+
+    mkdir("com.example");
+    touch("com.example/normal", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kTbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Age) {
+    LOG(INFO) << "FreeCache_Age";
+
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", kMbInBytes, 60);
+    touch("com.example/cache/foo/two", kMbInBytes, 120);
+
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Tombstone) {
+    LOG(INFO) << "FreeCache_Tombstone";
+
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache/bar");
+    touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120);
+    touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120);
+
+    setxattr("com.example/cache/bar", "user.cache_tombstone");
+
+    EXPECT_EQ(0, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/foo2"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+    EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
+    EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
+
+    service->freeCache(testUuid, kTbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+    EXPECT_EQ(0, size("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, size("com.example/cache/bar/bar2"));
+}
+
+TEST_F(CacheTest, FreeCache_Group) {
+    LOG(INFO) << "FreeCache_Group";
+
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120);
+
+    setxattr("com.example/cache/foo", "user.cache_group");
+
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+}
+
+TEST_F(CacheTest, FreeCache_GroupTombstone) {
+    LOG(INFO) << "FreeCache_GroupTombstone";
+
+    mkdir("com.example");
+    mkdir("com.example/cache");
+
+    // this dir must look really old for some reason?
+    mkdir("com.example/cache/group");
+    touch("com.example/cache/group/file1", kMbInBytes, 120);
+    touch("com.example/cache/group/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/group/dir");
+    touch("com.example/cache/group/dir/file1", kMbInBytes, 120);
+    touch("com.example/cache/group/dir/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/group/tomb");
+    touch("com.example/cache/group/tomb/file1", kMbInBytes, 120);
+    touch("com.example/cache/group/tomb/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/group/tomb/dir");
+    touch("com.example/cache/group/tomb/dir/file1", kMbInBytes, 120);
+    touch("com.example/cache/group/tomb/dir/file2", kMbInBytes, 120);
+
+    mkdir("com.example/cache/tomb");
+    touch("com.example/cache/tomb/file1", kMbInBytes, 240);
+    touch("com.example/cache/tomb/file2", kMbInBytes, 240);
+    mkdir("com.example/cache/tomb/dir");
+    touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240);
+    touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240);
+    mkdir("com.example/cache/tomb/group");
+    touch("com.example/cache/tomb/group/file1", kMbInBytes, 60);
+    touch("com.example/cache/tomb/group/file2", kMbInBytes, 60);
+    mkdir("com.example/cache/tomb/group/dir");
+    touch("com.example/cache/tomb/group/dir/file1", kMbInBytes, 60);
+    touch("com.example/cache/tomb/group/dir/file2", kMbInBytes, 60);
+
+    setxattr("com.example/cache/group", "user.cache_group");
+    setxattr("com.example/cache/group/tomb", "user.cache_tombstone");
+    setxattr("com.example/cache/tomb", "user.cache_tombstone");
+    setxattr("com.example/cache/tomb/group", "user.cache_group");
+
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file2"));
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, size("com.example/cache/group/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/group/file2"));
+    EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+
+    service->freeCache(testUuid, kTbInBytes, 0,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, size("com.example/cache/group/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/group/file2"));
+    EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
+
+    EXPECT_EQ(0, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
new file mode 100644
index 0000000..34818f6
--- /dev/null
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+constexpr const char* kTestUuid = "TEST";
+
+static constexpr int FLAG_FORCE = 1 << 16;
+
+int get_property(const char *key, char *value, const char *default_value) {
+    return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *oat_dir ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX],
+        const char *src,
+        const char *instruction_set) {
+    // Not really a valid path but it's good enough for testing.
+    sprintf(path,"/data/dalvik-cache/%s/%s", instruction_set, src);
+    return true;
+}
+
+static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    ::mkdir(fullPath, mode);
+    ::chown(fullPath, owner, group);
+    ::chmod(fullPath, mode);
+}
+
+static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+    int fd = ::open(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(),
+            O_RDWR | O_CREAT, mode);
+    ::fchown(fd, owner, group);
+    ::fchmod(fd, mode);
+    ::close(fd);
+}
+
+static int stat_gid(const char* path) {
+    struct stat buf;
+    ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+    return buf.st_gid;
+}
+
+static int stat_mode(const char* path) {
+    struct stat buf;
+    ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+    return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+}
+
+class ServiceTest : public testing::Test {
+protected:
+    InstalldNativeService* service;
+    std::unique_ptr<std::string> testUuid;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service = new InstalldNativeService();
+        testUuid = std::make_unique<std::string>();
+        *testUuid = std::string(kTestUuid);
+        system("mkdir -p /data/local/tmp/user/0");
+    }
+
+    virtual void TearDown() {
+        delete service;
+        system("rm -rf /data/local/tmp/user");
+    }
+};
+
+TEST_F(ServiceTest, FixupAppData_Upgrade) {
+    LOG(INFO) << "FixupAppData_Upgrade";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/normal", 10000, 10000, 0700);
+    mkdir("com.example/cache", 10000, 10000, 0700);
+    touch("com.example/cache/file", 10000, 10000, 0700);
+
+    service->fixupAppData(testUuid, 0);
+
+    EXPECT_EQ(10000, stat_gid("com.example/normal"));
+    EXPECT_EQ(20000, stat_gid("com.example/cache"));
+    EXPECT_EQ(20000, stat_gid("com.example/cache/file"));
+
+    EXPECT_EQ(0700, stat_mode("com.example/normal"));
+    EXPECT_EQ(02771, stat_mode("com.example/cache"));
+    EXPECT_EQ(0700, stat_mode("com.example/cache/file"));
+}
+
+TEST_F(ServiceTest, FixupAppData_Moved) {
+    LOG(INFO) << "FixupAppData_Moved";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("com.example/bar", 10000, 20000, 0700);
+    touch("com.example/bar/file", 10000, 20000, 0700);
+
+    service->fixupAppData(testUuid, 0);
+
+    EXPECT_EQ(10000, stat_gid("com.example/foo"));
+    EXPECT_EQ(20000, stat_gid("com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+
+    service->fixupAppData(testUuid, FLAG_FORCE);
+
+    EXPECT_EQ(10000, stat_gid("com.example/foo"));
+    EXPECT_EQ(10000, stat_gid("com.example/foo/file"));
+    EXPECT_EQ(10000, stat_gid("com.example/bar"));
+    EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
+}
+
+TEST_F(ServiceTest, RmDexNoDalvikCache) {
+    LOG(INFO) << "RmDexNoDalvikCache";
+
+    // Try to remove a non existing dalvik cache dex. The call should be
+    // successful because there's nothing to remove.
+    EXPECT_TRUE(service->rmdex("com.example", "arm").isOk());
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 9b2de88..dab3236 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -19,9 +19,9 @@
 
 #include <gtest/gtest.h>
 
-#include <commands.h>
-#include <globals.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
 
 #undef LOG_TAG
 #define LOG_TAG "utils_test"
@@ -29,12 +29,15 @@
 #define TEST_DATA_DIR "/data/"
 #define TEST_APP_DIR "/data/app/"
 #define TEST_APP_PRIVATE_DIR "/data/app-private/"
+#define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/"
 #define TEST_ASEC_DIR "/mnt/asec/"
 #define TEST_EXPAND_DIR "/mnt/expand/"
 
 #define TEST_SYSTEM_DIR1 "/system/app/"
 #define TEST_SYSTEM_DIR2 "/vendor/app/"
 
+#define TEST_PROFILE_DIR "/data/misc/profiles"
+
 #define REALLY_LONG_APP_NAME "com.example." \
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
@@ -57,6 +60,9 @@
         android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR;
         android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
 
+        android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR;
+        android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR);
+
         android_data_dir.path = (char*) TEST_DATA_DIR;
         android_data_dir.len = strlen(TEST_DATA_DIR);
 
@@ -74,6 +80,9 @@
 
         android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
         android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+
+        android_profiles_dir.path = (char*) TEST_PROFILE_DIR;
+        android_profiles_dir.len = strlen(TEST_PROFILE_DIR);
     }
 
     virtual void TearDown() {
@@ -85,19 +94,19 @@
     // Bad prefixes directories
     const char *badprefix1 = "/etc/passwd";
     EXPECT_EQ(-1, validate_apk_path(badprefix1))
-            << badprefix1 << " should be allowed as a valid path";
+            << badprefix1 << " should not be allowed as a valid path";
 
     const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah";
     EXPECT_EQ(-1, validate_apk_path(badprefix2))
-            << badprefix2 << " should be allowed as a valid path";
+            << badprefix2 << " should not be allowed as a valid path";
 
     const char *badprefix3 = "init.rc";
     EXPECT_EQ(-1, validate_apk_path(badprefix3))
-            << badprefix3 << " should be allowed as a valid path";
+            << badprefix3 << " should not be allowed as a valid path";
 
     const char *badprefix4 = "/init.rc";
     EXPECT_EQ(-1, validate_apk_path(badprefix4))
-            << badprefix4 << " should be allowed as a valid path";
+            << badprefix4 << " should not be allowed as a valid path";
 }
 
 TEST_F(UtilsTest, IsValidApkPath_Internal) {
@@ -317,6 +326,7 @@
     size_t pkgnameSize = PKG_NAME_MAX;
     char pkgname[pkgnameSize + 1];
     memset(pkgname, 'a', pkgnameSize);
+    pkgname[1] = '.';
     pkgname[pkgnameSize] = '\0';
 
     EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
@@ -329,19 +339,6 @@
              << "Package path should be a really long string of a's";
 }
 
-TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) {
-    char path[PKG_PATH_MAX];
-
-    // Create long packagename of "aaaaa..."
-    size_t pkgnameSize = PKG_NAME_MAX + 1;
-    char pkgname[pkgnameSize + 1];
-    memset(pkgname, 'a', pkgnameSize);
-    pkgname[pkgnameSize] = '\0';
-
-    EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0))
-            << "Should return error because package name is too long.";
-}
-
 TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
     char path[PKG_PATH_MAX];
 
@@ -508,5 +505,71 @@
             create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
 }
 
+TEST_F(UtilsTest, IsValidPackageName) {
+    EXPECT_EQ(true, is_valid_package_name("android"));
+    EXPECT_EQ(true, is_valid_package_name("com.example"));
+    EXPECT_EQ(true, is_valid_package_name("com.example-1"));
+    EXPECT_EQ(true, is_valid_package_name("com.example-1024"));
+    EXPECT_EQ(true, is_valid_package_name("com.example.foo---KiJFj4a_tePVw95pSrjg=="));
+    EXPECT_EQ(true, is_valid_package_name("really_LONG.a1234.package_name"));
+
+    EXPECT_EQ(false, is_valid_package_name("1234.package"));
+    EXPECT_EQ(false, is_valid_package_name("com.1234.package"));
+    EXPECT_EQ(false, is_valid_package_name(""));
+    EXPECT_EQ(false, is_valid_package_name("."));
+    EXPECT_EQ(false, is_valid_package_name(".."));
+    EXPECT_EQ(false, is_valid_package_name("../"));
+    EXPECT_EQ(false, is_valid_package_name("com.example/../com.evil/"));
+    EXPECT_EQ(false, is_valid_package_name("com.example-1/../com.evil/"));
+    EXPECT_EQ(false, is_valid_package_name("/com.evil"));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePath) {
+    EXPECT_EQ("/data/misc/profiles/cur/0", create_primary_cur_profile_dir_path(0));
+    EXPECT_EQ("/data/misc/profiles/cur/1", create_primary_cur_profile_dir_path(1));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePackagePath) {
+    EXPECT_EQ("/data/misc/profiles/cur/0/com.example",
+            create_primary_current_profile_package_dir_path(0, "com.example"));
+    EXPECT_EQ("/data/misc/profiles/cur/1/com.example",
+            create_primary_current_profile_package_dir_path(1, "com.example"));
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePath) {
+    EXPECT_EQ("/data/misc/profiles/ref", create_primary_ref_profile_dir_path());
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePackagePath) {
+    EXPECT_EQ("/data/misc/profiles/ref/com.example",
+        create_primary_reference_profile_package_dir_path("com.example"));
+}
+
+TEST_F(UtilsTest, CreatePrimaryCurrentProfile) {
+    std::string expected =
+        create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof";
+    EXPECT_EQ(expected,
+            create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false));
+}
+
+TEST_F(UtilsTest, CreatePrimaryReferenceProfile) {
+    std::string expected =
+        create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof";
+    EXPECT_EQ(expected,
+            create_reference_profile_path("com.example", /*is_secondary*/false));
+}
+
+TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {
+    EXPECT_EQ("/data/user/0/com.example/secondary.dex.prof",
+            create_current_profile_path(/*user*/0,
+                    "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
+}
+
+TEST_F(UtilsTest, CreateSecondaryReferenceProfile) {
+    EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof",
+            create_reference_profile_path(
+                    "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 674f760..c792082 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -18,21 +18,18 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
-
-#if defined(__APPLE__)
-#include <sys/mount.h>
-#else
-#include <sys/statfs.h>
-#endif
+#include <sys/statvfs.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
 #include "globals.h"  // extern variables.
@@ -41,7 +38,6 @@
 #define LOG_TAG "installd"
 #endif
 
-#define CACHE_NOISY(x) //x
 #define DEBUG_XATTRS 0
 
 using android::base::StringPrintf;
@@ -53,7 +49,7 @@
  * Check that given string is valid filename, and that it attempts no
  * parent or child directory traversal.
  */
-static bool is_valid_filename(const std::string& name) {
+bool is_valid_filename(const std::string& name) {
     if (name.empty() || (name == ".") || (name == "..")
             || (name.find('/') != std::string::npos)) {
         return false;
@@ -64,7 +60,7 @@
 
 static void check_package_name(const char* package_name) {
     CHECK(is_valid_filename(package_name));
-    CHECK(is_valid_package_name(package_name) == 0);
+    CHECK(is_valid_package_name(package_name));
 }
 
 /**
@@ -135,7 +131,7 @@
 
 int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname,
         const char *postfix, userid_t userid) {
-    if (is_valid_package_name(pkgname) != 0) {
+    if (!is_valid_package_name(pkgname)) {
         path[0] = '\0';
         return -1;
     }
@@ -154,6 +150,9 @@
 std::string create_data_path(const char* volume_uuid) {
     if (volume_uuid == nullptr) {
         return "/data";
+    } else if (!strcmp(volume_uuid, "TEST")) {
+        CHECK(property_get_bool("ro.debuggable", false));
+        return "/data/local/tmp";
     } else {
         CHECK(is_valid_filename(volume_uuid));
         return StringPrintf("/mnt/expand/%s", volume_uuid);
@@ -198,22 +197,76 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) {
+    return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name);
+}
+
+std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
+        const char* data_type, const char* package_name) {
+    return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(),
+            data_type, package_name);
+}
+
 std::string create_data_misc_legacy_path(userid_t userid) {
     return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
 }
 
-std::string create_data_user_profiles_path(userid_t userid) {
+std::string create_primary_cur_profile_dir_path(userid_t userid) {
     return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
 }
 
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
-    check_package_name(package_name);
-    return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name);
+std::string create_primary_current_profile_package_dir_path(userid_t user,
+        const std::string& package_name) {
+    check_package_name(package_name.c_str());
+    return StringPrintf("%s/%s",
+            create_primary_cur_profile_dir_path(user).c_str(), package_name.c_str());
 }
 
-std::string create_data_ref_profile_package_path(const char* package_name) {
-    check_package_name(package_name);
-    return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
+std::string create_primary_ref_profile_dir_path() {
+    return StringPrintf("%s/ref", android_profiles_dir.path);
+}
+
+std::string create_primary_reference_profile_package_dir_path(const std::string& package_name) {
+    check_package_name(package_name.c_str());
+    return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str());
+}
+
+std::string create_data_dalvik_cache_path() {
+    return "/data/dalvik-cache";
+}
+
+// Keep profile paths in sync with ActivityThread and LoadedApk.
+const std::string PROFILE_EXT = ".prof";
+const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT;
+
+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());
+    } else {
+        // Profiles for primary apks are under /data/misc/profiles/cur.
+        std::string profile_dir = create_primary_current_profile_package_dir_path(user, location);
+        return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+    }
+}
+
+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)
+                << "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 {
+        // Reference profiles for primary apks are stored in /data/misc/profile/ref.
+        std::string profile_dir = create_primary_reference_profile_package_dir_path(location);
+        return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+    }
 }
 
 std::vector<userid_t> get_known_users(const char* volume_uuid) {
@@ -248,6 +301,59 @@
     return users;
 }
 
+int calculate_tree_size(const std::string& path, int64_t* size,
+        int32_t include_gid, int32_t exclude_gid, bool exclude_apps) {
+    FTS *fts;
+    FTSENT *p;
+    int64_t matchedSize = 0;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Failed to fts_open " << path;
+        }
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_D:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE:
+            int32_t uid = p->fts_statp->st_uid;
+            int32_t gid = p->fts_statp->st_gid;
+            int32_t user_uid = multiuser_get_app_id(uid);
+            int32_t user_gid = multiuser_get_app_id(gid);
+            if (exclude_apps && ((user_uid >= AID_APP_START && user_uid <= AID_APP_END)
+                    || (user_gid >= AID_CACHE_GID_START && user_gid <= AID_CACHE_GID_END)
+                    || (user_gid >= AID_SHARED_GID_START && user_gid <= AID_SHARED_GID_END))) {
+                // Don't traverse inside or measure
+                fts_set(fts, p, FTS_SKIP);
+                break;
+            }
+            if (include_gid != -1 && gid != include_gid) {
+                break;
+            }
+            if (exclude_gid != -1 && gid == exclude_gid) {
+                break;
+            }
+            matchedSize += (p->fts_statp->st_blocks * 512);
+            break;
+        }
+    }
+    fts_close(fts);
+#if MEASURE_DEBUG
+    if ((include_gid == -1) && (exclude_gid == -1)) {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize;
+    } else {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize << "; include " << include_gid
+                << " exclude " << exclude_gid;
+    }
+#endif
+    *size += matchedSize;
+    return 0;
+}
+
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
@@ -266,49 +372,46 @@
  * Checks whether the package name is valid. Returns -1 on error and
  * 0 on success.
  */
-int is_valid_package_name(const char* pkgname) {
-    const char *x = pkgname;
-    int alpha = -1;
+bool is_valid_package_name(const std::string& packageName) {
+    // This logic is borrowed from PackageParser.java
+    bool hasSep = false;
+    bool front = true;
 
-    if (strlen(pkgname) > PKG_NAME_MAX) {
-        return -1;
-    }
-
-    while (*x) {
-        if (isalnum(*x) || (*x == '_')) {
-                /* alphanumeric or underscore are fine */
-        } else if (*x == '.') {
-            if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
-                    /* periods must not be first, last, or doubled */
-                ALOGE("invalid package name '%s'\n", pkgname);
-                return -1;
-            }
-        } else if (*x == '-') {
-            /* Suffix -X is fine to let versioning of packages.
-               But whatever follows should be alphanumeric.*/
-            alpha = 1;
-        } else {
-                /* anything not A-Z, a-z, 0-9, _, or . is invalid */
-            ALOGE("invalid package name '%s'\n", pkgname);
-            return -1;
+    auto it = packageName.begin();
+    for (; it != packageName.end() && *it != '-'; it++) {
+        char c = *it;
+        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+            front = false;
+            continue;
         }
-
-        x++;
-    }
-
-    if (alpha == 1) {
-        // Skip current character
-        x++;
-        while (*x) {
-            if (!isalnum(*x)) {
-                ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
-                return -1;
+        if (!front) {
+            if ((c >= '0' && c <= '9') || c == '_') {
+                continue;
             }
-            x++;
         }
+        if (c == '.') {
+            hasSep = true;
+            front = true;
+            continue;
+        }
+        LOG(WARNING) << "Bad package character " << c << " in " << packageName;
+        return false;
     }
 
-    return 0;
+    if (front) {
+        LOG(WARNING) << "Missing separator in " << packageName;
+        return false;
+    }
+
+    for (; it != packageName.end(); it++) {
+        char c = *it;
+        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue;
+        if ((c >= '0' && c <= '9') || c == '_' || c == '-' || c == '=') continue;
+        LOG(WARNING) << "Bad suffix character " << c << " in " << packageName;
+        return false;
+    }
+
+    return true;
 }
 
 static int _delete_dir_contents(DIR *d,
@@ -526,264 +629,16 @@
     return res;
 }
 
-int64_t data_disk_free(const std::string& data_path)
-{
-    struct statfs sfs;
-    if (statfs(data_path.c_str(), &sfs) == 0) {
-        return sfs.f_bavail * sfs.f_bsize;
+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;
     } else {
-        PLOG(ERROR) << "Couldn't statfs " << data_path;
+        PLOG(ERROR) << "Couldn't statvfs " << data_path;
         return -1;
     }
 }
 
-cache_t* start_cache_collection()
-{
-    cache_t* cache = (cache_t*)calloc(1, sizeof(cache_t));
-    return cache;
-}
-
-#define CACHE_BLOCK_SIZE (512*1024)
-
-static void* _cache_malloc(cache_t* cache, size_t len)
-{
-    len = (len+3)&~3;
-    if (len > (CACHE_BLOCK_SIZE/2)) {
-        // It doesn't make sense to try to put this allocation into one
-        // of our blocks, because it is so big.  Instead, make a new dedicated
-        // block for it.
-        int8_t* res = (int8_t*)malloc(len+sizeof(void*));
-        if (res == NULL) {
-            return NULL;
-        }
-        CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %zu", res, len));
-        // Link it into our list of blocks, not disrupting the current one.
-        if (cache->memBlocks == NULL) {
-            *(void**)res = NULL;
-            cache->memBlocks = res;
-        } else {
-            *(void**)res = *(void**)cache->memBlocks;
-            *(void**)cache->memBlocks = res;
-        }
-        return res + sizeof(void*);
-    }
-    int8_t* res = cache->curMemBlockAvail;
-    int8_t* nextPos = res + len;
-    if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) {
-        int8_t* newBlock = (int8_t*) malloc(CACHE_BLOCK_SIZE);
-        if (newBlock == NULL) {
-            return NULL;
-        }
-        CACHE_NOISY(ALOGI("Allocated new cache mem block: %p", newBlock));
-        *(void**)newBlock = cache->memBlocks;
-        cache->memBlocks = newBlock;
-        res = cache->curMemBlockAvail = newBlock + sizeof(void*);
-        cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE;
-        nextPos = res + len;
-    }
-    CACHE_NOISY(ALOGI("cache_malloc: ret %p size %zu, block=%p, nextPos=%p",
-            res, len, cache->memBlocks, nextPos));
-    cache->curMemBlockAvail = nextPos;
-    return res;
-}
-
-static void* _cache_realloc(cache_t* cache, void* cur, size_t origLen, size_t len)
-{
-    // This isn't really a realloc, but it is good enough for our purposes here.
-    void* alloc = _cache_malloc(cache, len);
-    if (alloc != NULL && cur != NULL) {
-        memcpy(alloc, cur, origLen < len ? origLen : len);
-    }
-    return alloc;
-}
-
-static void _inc_num_cache_collected(cache_t* cache)
-{
-    cache->numCollected++;
-    if ((cache->numCollected%20000) == 0) {
-        ALOGI("Collected cache so far: %zd directories, %zd files",
-            cache->numDirs, cache->numFiles);
-    }
-}
-
-static cache_dir_t* _add_cache_dir_t(cache_t* cache, cache_dir_t* parent, const char *name)
-{
-    size_t nameLen = strlen(name);
-    cache_dir_t* dir = (cache_dir_t*)_cache_malloc(cache, sizeof(cache_dir_t)+nameLen+1);
-    if (dir != NULL) {
-        dir->parent = parent;
-        dir->childCount = 0;
-        dir->hiddenCount = 0;
-        dir->deleted = 0;
-        strcpy(dir->name, name);
-        if (cache->numDirs >= cache->availDirs) {
-            size_t newAvail = cache->availDirs < 1000 ? 1000 : cache->availDirs*2;
-            cache_dir_t** newDirs = (cache_dir_t**)_cache_realloc(cache, cache->dirs,
-                    cache->availDirs*sizeof(cache_dir_t*), newAvail*sizeof(cache_dir_t*));
-            if (newDirs == NULL) {
-                ALOGE("Failure growing cache dirs array for %s\n", name);
-                return NULL;
-            }
-            cache->availDirs = newAvail;
-            cache->dirs = newDirs;
-        }
-        cache->dirs[cache->numDirs] = dir;
-        cache->numDirs++;
-        if (parent != NULL) {
-            parent->childCount++;
-        }
-        _inc_num_cache_collected(cache);
-    } else {
-        ALOGE("Failure allocating cache_dir_t for %s\n", name);
-    }
-    return dir;
-}
-
-static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t modTime,
-        const char *name)
-{
-    size_t nameLen = strlen(name);
-    cache_file_t* file = (cache_file_t*)_cache_malloc(cache, sizeof(cache_file_t)+nameLen+1);
-    if (file != NULL) {
-        file->dir = dir;
-        file->modTime = modTime;
-        strcpy(file->name, name);
-        if (cache->numFiles >= cache->availFiles) {
-            size_t newAvail = cache->availFiles < 1000 ? 1000 : cache->availFiles*2;
-            cache_file_t** newFiles = (cache_file_t**)_cache_realloc(cache, cache->files,
-                    cache->availFiles*sizeof(cache_file_t*), newAvail*sizeof(cache_file_t*));
-            if (newFiles == NULL) {
-                ALOGE("Failure growing cache file array for %s\n", name);
-                return NULL;
-            }
-            cache->availFiles = newAvail;
-            cache->files = newFiles;
-        }
-        CACHE_NOISY(ALOGI("Setting file %p at position %zd in array %p", file,
-                cache->numFiles, cache->files));
-        cache->files[cache->numFiles] = file;
-        cache->numFiles++;
-        dir->childCount++;
-        _inc_num_cache_collected(cache);
-    } else {
-        ALOGE("Failure allocating cache_file_t for %s\n", name);
-    }
-    return file;
-}
-
-static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName,
-        DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen)
-{
-    struct dirent *de;
-    cache_dir_t* cacheDir = NULL;
-    int dfd;
-
-    CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s",
-            parentDir, dirName, dir, pathBase));
-
-    dfd = dirfd(dir);
-
-    if (dfd < 0) return 0;
-
-    // Sub-directories always get added to the data structure, so if they
-    // are empty we will know about them to delete them later.
-    cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
-
-    while ((de = readdir(dir))) {
-        const char *name = de->d_name;
-
-        if (de->d_type == DT_DIR) {
-            int subfd;
-            DIR *subdir;
-
-                /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
-
-            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
-            if (subfd < 0) {
-                ALOGE("Couldn't openat %s: %s\n", name, strerror(errno));
-                continue;
-            }
-            subdir = fdopendir(subfd);
-            if (subdir == NULL) {
-                ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno));
-                close(subfd);
-                continue;
-            }
-            if (cacheDir == NULL) {
-                cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
-            }
-            if (cacheDir != NULL) {
-                // Update pathBase for the new path...  this may change dirName
-                // if that is also pointing to the path, but we are done with it
-                // now.
-                size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
-                CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase));
-                if (finallen < pathAvailLen) {
-                    _add_cache_files(cache, cacheDir, name, subdir, pathBase,
-                            pathPos+finallen, pathAvailLen-finallen);
-                } else {
-                    // Whoops, the final path is too long!  We'll just delete
-                    // this directory.
-                    ALOGW("Cache dir %s truncated in path %s; deleting dir\n",
-                            name, pathBase);
-                    _delete_dir_contents(subdir, NULL);
-                    if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
-                        ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno));
-                    }
-                }
-            }
-            closedir(subdir);
-        } else if (de->d_type == DT_REG) {
-            // Skip files that start with '.'; they will be deleted if
-            // their entire directory is deleted.  This allows for metadata
-            // like ".nomedia" to remain in the directory until the entire
-            // directory is deleted.
-            if (cacheDir == NULL) {
-                cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
-            }
-            if (name[0] == '.') {
-                cacheDir->hiddenCount++;
-                continue;
-            }
-            if (cacheDir != NULL) {
-                // Build final full path for file...  this may change dirName
-                // if that is also pointing to the path, but we are done with it
-                // now.
-                size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
-                CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase));
-                if (finallen < pathAvailLen) {
-                    struct stat s;
-                    if (stat(pathBase, &s) >= 0) {
-                        _add_cache_file_t(cache, cacheDir, s.st_mtime, name);
-                    } else {
-                        ALOGW("Unable to stat cache file %s; deleting\n", pathBase);
-                        if (unlink(pathBase) < 0) {
-                            ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno));
-                        }
-                    }
-                } else {
-                    // Whoops, the final path is too long!  We'll just delete
-                    // this file.
-                    ALOGW("Cache file %s truncated in path %s; deleting\n",
-                            name, pathBase);
-                    if (unlinkat(dfd, name, 0) < 0) {
-                        *pathPos = 0;
-                        ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase,
-                                strerror(errno));
-                    }
-                }
-            }
-        } else {
-            cacheDir->hiddenCount++;
-        }
-    }
-    return 0;
-}
-
 int get_path_inode(const std::string& path, ino_t *inode) {
     struct stat buf;
     memset(&buf, 0, sizeof(buf));
@@ -877,172 +732,6 @@
     }
 }
 
-void add_cache_files(cache_t* cache, const std::string& data_path) {
-    DIR *d;
-    struct dirent *de;
-    char dirname[PATH_MAX];
-
-    const char* basepath = data_path.c_str();
-    CACHE_NOISY(ALOGI("add_cache_files: basepath=%s\n", basepath));
-
-    d = opendir(basepath);
-    if (d == NULL) {
-        return;
-    }
-
-    while ((de = readdir(d))) {
-        if (de->d_type == DT_DIR) {
-            DIR* subdir;
-            const char *name = de->d_name;
-
-                /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
-
-            auto parent = StringPrintf("%s/%s", basepath, name);
-            auto resolved = read_path_inode(parent, "cache", kXattrInodeCache);
-            strcpy(dirname, resolved.c_str());
-            CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
-
-            subdir = opendir(dirname);
-            if (subdir != NULL) {
-                size_t dirnameLen = strlen(dirname);
-                _add_cache_files(cache, NULL, dirname, subdir, dirname, dirname+dirnameLen,
-                        PATH_MAX - dirnameLen);
-                closedir(subdir);
-            }
-        }
-    }
-
-    closedir(d);
-}
-
-static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir)
-{
-    char *pos = path;
-    if (dir->parent != NULL) {
-        pos = create_dir_path(path, dir->parent);
-    }
-    // Note that we don't need to worry about going beyond the buffer,
-    // since when we were constructing the cache entries our maximum
-    // buffer size for full paths was PATH_MAX.
-    strcpy(pos, dir->name);
-    pos += strlen(pos);
-    *pos = '/';
-    pos++;
-    *pos = 0;
-    return pos;
-}
-
-static void delete_cache_dir(char path[PATH_MAX], cache_dir_t* dir)
-{
-    if (dir->parent != NULL) {
-        create_dir_path(path, dir);
-        ALOGI("DEL DIR %s\n", path);
-        if (dir->hiddenCount <= 0) {
-            if (rmdir(path)) {
-                ALOGE("Couldn't rmdir %s: %s\n", path, strerror(errno));
-                return;
-            }
-        } else {
-            // The directory contains hidden files so we need to delete
-            // them along with the directory itself.
-            if (delete_dir_contents(path, 1, NULL)) {
-                return;
-            }
-        }
-        dir->parent->childCount--;
-        dir->deleted = 1;
-        if (dir->parent->childCount <= 0) {
-            delete_cache_dir(path, dir->parent);
-        }
-    } else if (dir->hiddenCount > 0) {
-        // This is a root directory, but it has hidden files.  Get rid of
-        // all of those files, but not the directory itself.
-        create_dir_path(path, dir);
-        ALOGI("DEL CONTENTS %s\n", path);
-        delete_dir_contents(path, 0, NULL);
-    }
-}
-
-static int cache_modtime_sort(const void *lhsP, const void *rhsP)
-{
-    const cache_file_t *lhs = *(const cache_file_t**)lhsP;
-    const cache_file_t *rhs = *(const cache_file_t**)rhsP;
-    return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0);
-}
-
-void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size)
-{
-    size_t i;
-    int skip = 0;
-    char path[PATH_MAX];
-
-    ALOGI("Collected cache files: %zd directories, %zd files",
-        cache->numDirs, cache->numFiles);
-
-    CACHE_NOISY(ALOGI("Sorting files..."));
-    qsort(cache->files, cache->numFiles, sizeof(cache_file_t*),
-            cache_modtime_sort);
-
-    CACHE_NOISY(ALOGI("Cleaning empty directories..."));
-    for (i=cache->numDirs; i>0; i--) {
-        cache_dir_t* dir = cache->dirs[i-1];
-        if (dir->childCount <= 0 && !dir->deleted) {
-            delete_cache_dir(path, dir);
-        }
-    }
-
-    CACHE_NOISY(ALOGI("Trimming files..."));
-    for (i=0; i<cache->numFiles; i++) {
-        skip++;
-        if (skip > 10) {
-            if (data_disk_free(data_path) > free_size) {
-                return;
-            }
-            skip = 0;
-        }
-        cache_file_t* file = cache->files[i];
-        strcpy(create_dir_path(path, file->dir), file->name);
-        ALOGI("DEL (mod %d) %s\n", (int)file->modTime, path);
-        if (unlink(path) < 0) {
-            ALOGE("Couldn't unlink %s: %s\n", path, strerror(errno));
-        }
-        file->dir->childCount--;
-        if (file->dir->childCount <= 0) {
-            delete_cache_dir(path, file->dir);
-        }
-    }
-}
-
-void finish_cache_collection(cache_t* cache)
-{
-    CACHE_NOISY(size_t i;)
-
-    CACHE_NOISY(ALOGI("clear_cache_files: %zu dirs, %zu files\n", cache->numDirs, cache->numFiles));
-    CACHE_NOISY(
-        for (i=0; i<cache->numDirs; i++) {
-            cache_dir_t* dir = cache->dirs[i];
-            ALOGI("dir #%zu: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
-        })
-    CACHE_NOISY(
-        for (i=0; i<cache->numFiles; i++) {
-            cache_file_t* file = cache->files[i];
-            ALOGI("file #%zu: %p %s time=%d dir=%p\n", i, file, file->name,
-                    (int)file->modTime, file->dir);
-        })
-    void* block = cache->memBlocks;
-    while (block != NULL) {
-        void* nextBlock = *(void**)block;
-        CACHE_NOISY(ALOGI("Freeing cache mem block: %p", block));
-        free(block);
-        block = nextBlock;
-    }
-    free(cache);
-}
-
 /**
  * Validate that the path is valid in the context of the provided directory.
  * The path is allowed to have at most one subdirectory and no indirections
@@ -1087,6 +776,27 @@
     return -1;
 }
 
+bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
+        const char* volume_uuid, int uid, int storage_flag) {
+    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;
+    }
+    // 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;
+}
+
 /**
  * Get the contents of a environment variable that contains a path. Caller
  * owns the string that is inserted into the directory record. Returns
@@ -1286,5 +996,77 @@
     }
 }
 
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ * The app cache directory path will be 'parent'/'name'.
+ */
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid) {
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+            // This is fine, just create it
+            if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << path;
+                return -1;
+            } else {
+                return 0;
+            }
+        } else {
+            PLOG(ERROR) << "Failed to stat " << path;
+            return -1;
+        }
+    }
+
+    mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+    if (st.st_uid != uid) {
+        // Mismatched UID is real trouble; we can't recover
+        LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
+                << " but expected " << uid;
+        return -1;
+    } else if (st.st_gid == gid && actual_mode == target_mode) {
+        // Everything looks good!
+        return 0;
+    } else {
+        // Mismatched GID/mode is recoverable; fall through to update
+        LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid
+                << " but expected " << gid;
+    }
+
+    // Directory is owned correctly, but GID or mode mismatch means it's
+    // probably a platform upgrade so we need to fix them
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
+        PLOG(ERROR) << "Failed to fts_open " << path;
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_DP:
+            if (chmod(p->fts_path, target_mode) != 0) {
+                PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+            }
+            // Intentional fall through to also set GID
+        case FTS_F:
+            if (chown(p->fts_path, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        case FTS_SL:
+        case FTS_SLNONE:
+            if (lchown(p->fts_path, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        }
+    }
+    fts_close(fts);
+    return 0;
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 8123e9b..070da84 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -30,40 +30,21 @@
 
 #include <installd_constants.h>
 
+#define MEASURE_DEBUG 0
+#define FIXUP_DEBUG 0
+
+#define BYPASS_QUOTA 0
+#define BYPASS_SDCARDFS 0
+
 namespace android {
 namespace installd {
 
 struct dir_rec_t;
 
-typedef struct cache_dir_struct {
-    struct cache_dir_struct* parent;
-    int32_t childCount;
-    int32_t hiddenCount;
-    int32_t deleted;
-    char name[];
-} cache_dir_t;
-
-typedef struct {
-    cache_dir_t* dir;
-    time_t modTime;
-    char name[];
-} cache_file_t;
-
-typedef struct {
-    size_t numDirs;
-    size_t availDirs;
-    cache_dir_t** dirs;
-    size_t numFiles;
-    size_t availFiles;
-    cache_file_t** files;
-    size_t numCollected;
-    void* memBlocks;
-    int8_t* curMemBlockAvail;
-    int8_t* curMemBlockEnd;
-} cache_t;
-
 constexpr const char* kXattrInodeCache = "user.inode_cache";
 constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
+constexpr const char* kXattrCacheGroup = "user.cache_group";
+constexpr const char* kXattrCacheTombstone = "user.cache_tombstone";
 
 int create_pkg_path(char path[PKG_PATH_MAX],
                     const char *pkgname,
@@ -73,7 +54,6 @@
 std::string create_data_path(const char* volume_uuid);
 
 std::string create_data_app_path(const char* volume_uuid);
-
 std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
 
 std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
@@ -87,15 +67,31 @@
         userid_t user, const char* package_name);
 
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
+std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
+        const char* data_type, const char* package_name);
 
 std::string create_data_misc_legacy_path(userid_t userid);
 
-std::string create_data_user_profiles_path(userid_t userid);
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
-std::string create_data_ref_profile_package_path(const char* package_name);
+std::string create_data_dalvik_cache_path();
+
+std::string create_primary_cur_profile_dir_path(userid_t userid);
+std::string create_primary_current_profile_package_dir_path(
+        userid_t user, const std::string& package_name);
+
+std::string create_primary_ref_profile_dir_path();
+std::string create_primary_reference_profile_package_dir_path(const std::string& package_name);
+
+std::string create_current_profile_path(
+        userid_t user, const std::string& package_name, bool is_secondary_dex);
+std::string create_reference_profile_path(
+        const std::string& package_name, bool is_secondary_dex);
 
 std::vector<userid_t> get_known_users(const char* volume_uuid);
 
+int calculate_tree_size(const std::string& path, int64_t* size,
+        int32_t include_gid = -1, int32_t exclude_gid = -1, bool exclude_apps = false);
+
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
 
 int create_move_path(char path[PKG_PATH_MAX],
@@ -103,7 +99,8 @@
                      const char* leaf,
                      userid_t userid);
 
-int is_valid_package_name(const char* pkgname);
+bool is_valid_filename(const std::string& name);
+bool is_valid_package_name(const std::string& packageName);
 
 int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
 int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
@@ -119,20 +116,14 @@
 
 int64_t data_disk_free(const std::string& data_path);
 
-cache_t* start_cache_collection();
-
 int get_path_inode(const std::string& path, ino_t *inode);
 
 int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
 std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
 
-void add_cache_files(cache_t* cache, const std::string& data_path);
-
-void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
-
-void finish_cache_collection(cache_t* cache);
-
 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);
 
 int get_path_from_env(dir_rec_t* rec, const char* var);
 
@@ -152,6 +143,9 @@
 
 int wait_child(pid_t pid);
 
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 75b907c..3b8955b 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -14,22 +14,22 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ip-up-vpn"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <linux/route.h>
+#include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <linux/if.h>
-#include <linux/route.h>
 
-#define LOG_TAG "ip-up-vpn"
-#include <cutils/log.h>
+#include <log/log.h>
 
 #define DIR "/data/misc/vpn/"
 
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
new file mode 100644
index 0000000..38647eb
--- /dev/null
+++ b/cmds/lshal/Android.bp
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "liblshal",
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhidl-gen-utils",
+        "libvintf",
+        "android.hidl.manager@1.0",
+    ],
+    srcs: [
+        "DebugCommand.cpp",
+        "Lshal.cpp",
+        "ListCommand.cpp",
+        "PipeRelay.cpp",
+        "utils.cpp",
+    ],
+}
+
+cc_defaults {
+    name: "lshal_defaults",
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "liblshal",
+        "libutils",
+    ]
+}
+
+cc_binary {
+    name: "lshal",
+    defaults: ["lshal_defaults"],
+    srcs: [
+        "main.cpp"
+    ]
+}
+
+cc_test {
+    name: "lshal_test",
+    defaults: ["lshal_defaults"],
+    gtest: true,
+    static_libs: [
+        "libgmock"
+    ],
+    shared_libs: [
+        "android.hardware.tests.baz@1.0"
+    ],
+    srcs: [
+        "test.cpp"
+    ]
+}
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
new file mode 100644
index 0000000..672cad6
--- /dev/null
+++ b/cmds/lshal/DebugCommand.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DebugCommand.h"
+
+#include "Lshal.h"
+
+namespace android {
+namespace lshal {
+
+DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) {
+}
+
+Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) {
+    if (optind >= arg.argc) {
+        mLshal.usage(command);
+        return USAGE;
+    }
+    mInterfaceName = arg.argv[optind];
+    ++optind;
+    for (; optind < arg.argc; ++optind) {
+        mOptions.push_back(arg.argv[optind]);
+    }
+    return OK;
+}
+
+Status DebugCommand::main(const std::string &command, const Arg &arg) {
+    Status status = parseArgs(command, arg);
+    if (status != OK) {
+        return status;
+    }
+    auto pair = splitFirst(mInterfaceName, '/');
+    return mLshal.emitDebugInfo(
+            pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
+            mLshal.out().buf(),
+            mLshal.err());
+}
+
+}  // namespace lshal
+}  // namespace android
+
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
new file mode 100644
index 0000000..fa0f0fa
--- /dev/null
+++ b/cmds/lshal/DebugCommand.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
+
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class DebugCommand {
+public:
+    DebugCommand(Lshal &lshal);
+    Status main(const std::string &command, const Arg &arg);
+private:
+    Status parseArgs(const std::string &command, const Arg &arg);
+
+    Lshal &mLshal;
+    std::string mInterfaceName;
+    std::vector<std::string> mOptions;
+
+    DISALLOW_COPY_AND_ASSIGN(DebugCommand);
+};
+
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
new file mode 100644
index 0000000..710b6e4
--- /dev/null
+++ b/cmds/lshal/ListCommand.cpp
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ListCommand.h"
+
+#include <getopt.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <regex>
+
+#include <android-base/parseint.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl-util/FQName.h>
+#include <private/android_filesystem_config.h>
+#include <sys/stat.h>
+#include <vintf/HalManifest.h>
+#include <vintf/parse_xml.h>
+
+#include "Lshal.h"
+#include "PipeRelay.h"
+#include "Timeout.h"
+#include "utils.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+namespace android {
+namespace lshal {
+
+ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
+}
+
+std::string getCmdline(pid_t pid) {
+    std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
+    std::string cmdline;
+    if (!ifs.is_open()) {
+        return "";
+    }
+    ifs >> cmdline;
+    return cmdline;
+}
+
+const std::string &ListCommand::getCmdline(pid_t pid) {
+    auto pair = mCmdlines.find(pid);
+    if (pair != mCmdlines.end()) {
+        return pair->second;
+    }
+    mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+    return mCmdlines[pid];
+}
+
+void ListCommand::removeDeadProcesses(Pids *pids) {
+    static const pid_t myPid = getpid();
+    pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
+        return pid == myPid || this->getCmdline(pid).empty();
+    }), pids->end());
+}
+
+bool ListCommand::getReferencedPids(
+        pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
+
+    std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
+    if (!ifs.is_open()) {
+        return false;
+    }
+
+    static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+
+    std::string line;
+    std::smatch match;
+    while(getline(ifs, line)) {
+        if (!std::regex_search(line, match, prefix)) {
+            // the line doesn't start with the correct prefix
+            continue;
+        }
+        std::string ptrString = "0x" + match.str(2); // use number after c
+        uint64_t ptr;
+        if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+            // Should not reach here, but just be tolerant.
+            mErr << "Could not parse number " << ptrString << std::endl;
+            continue;
+        }
+        const std::string proc = " proc ";
+        auto pos = line.rfind(proc);
+        if (pos != std::string::npos) {
+            for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
+                int32_t pid;
+                if (!::android::base::ParseInt(pidStr, &pid)) {
+                    mErr << "Could not parse number " << pidStr << std::endl;
+                    continue;
+                }
+                (*objects)[ptr].push_back(pid);
+            }
+        }
+    }
+    return true;
+}
+
+// Must process hwbinder services first, then passthrough services.
+void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
+    f(mServicesTable);
+    f(mPassthroughRefTable);
+    f(mImplementationsTable);
+}
+void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
+    f(mServicesTable);
+    f(mPassthroughRefTable);
+    f(mImplementationsTable);
+}
+
+void ListCommand::postprocess() {
+    forEachTable([this](Table &table) {
+        if (mSortColumn) {
+            std::sort(table.begin(), table.end(), mSortColumn);
+        }
+        for (TableEntry &entry : table) {
+            entry.serverCmdline = getCmdline(entry.serverPid);
+            removeDeadProcesses(&entry.clientPids);
+            for (auto pid : entry.clientPids) {
+                entry.clientCmdlines.push_back(this->getCmdline(pid));
+            }
+        }
+    });
+    // use a double for loop here because lshal doesn't care about efficiency.
+    for (TableEntry &packageEntry : mImplementationsTable) {
+        std::string packageName = packageEntry.interfaceName;
+        FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
+        if (!fqPackageName.isValid()) {
+            continue;
+        }
+        for (TableEntry &interfaceEntry : mPassthroughRefTable) {
+            if (interfaceEntry.arch != ARCH_UNKNOWN) {
+                continue;
+            }
+            FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
+            if (!interfaceName.isValid()) {
+                continue;
+            }
+            if (interfaceName.getPackageAndVersion() == fqPackageName) {
+                interfaceEntry.arch = packageEntry.arch;
+            }
+        }
+    }
+}
+
+void ListCommand::printLine(
+        const std::string &interfaceName,
+        const std::string &transport,
+        const std::string &arch,
+        const std::string &server,
+        const std::string &serverCmdline,
+        const std::string &address, const std::string &clients,
+        const std::string &clientCmdlines) const {
+    if (mSelectedColumns & ENABLE_INTERFACE_NAME)
+        mOut << std::setw(80) << interfaceName << "\t";
+    if (mSelectedColumns & ENABLE_TRANSPORT)
+        mOut << std::setw(10) << transport << "\t";
+    if (mSelectedColumns & ENABLE_ARCH)
+        mOut << std::setw(5) << arch << "\t";
+    if (mSelectedColumns & ENABLE_SERVER_PID) {
+        if (mEnableCmdlines) {
+            mOut << std::setw(15) << serverCmdline << "\t";
+        } else {
+            mOut << std::setw(5)  << server << "\t";
+        }
+    }
+    if (mSelectedColumns & ENABLE_SERVER_ADDR)
+        mOut << std::setw(16) << address << "\t";
+    if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
+        if (mEnableCmdlines) {
+            mOut << std::setw(0)  << clientCmdlines;
+        } else {
+            mOut << std::setw(0)  << clients;
+        }
+    }
+    mOut << std::endl;
+}
+
+void ListCommand::dumpVintf() const {
+    mOut << "<!-- " << std::endl
+         << "    This is a skeleton device manifest. Notes: " << std::endl
+         << "    1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
+         << "    2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
+         << "       only hwbinder is shown." << std::endl
+         << "    3. It is likely that HALs in passthrough transport does not have" << std::endl
+         << "       <interface> declared; users will have to write them by hand." << std::endl
+         << "    4. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
+         << "       is removed from the manifest file and written by assemble_vintf" << std::endl
+         << "       at build time." << std::endl
+         << "-->" << std::endl;
+
+    vintf::HalManifest manifest;
+    forEachTable([this, &manifest] (const Table &table) {
+        for (const TableEntry &entry : table) {
+
+            std::string fqInstanceName = entry.interfaceName;
+
+            if (&table == &mImplementationsTable) {
+                // Quick hack to work around *'s
+                replaceAll(&fqInstanceName, '*', 'D');
+            }
+            auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
+            FQName fqName(splittedFqInstanceName.first);
+            if (!fqName.isValid()) {
+                mErr << "Warning: '" << splittedFqInstanceName.first
+                     << "' is not a valid FQName." << std::endl;
+                continue;
+            }
+            // Strip out system libs.
+            if (fqName.inPackage("android.hidl") ||
+                fqName.inPackage("android.frameworks") ||
+                fqName.inPackage("android.system")) {
+                continue;
+            }
+            std::string interfaceName =
+                    &table == &mImplementationsTable ? "" : fqName.name();
+            std::string instanceName =
+                    &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
+
+            vintf::Version version{fqName.getPackageMajorVersion(),
+                                   fqName.getPackageMinorVersion()};
+            vintf::Transport transport;
+            vintf::Arch arch;
+            if (entry.transport == "hwbinder") {
+                transport = vintf::Transport::HWBINDER;
+                arch = vintf::Arch::ARCH_EMPTY;
+            } else if (entry.transport == "passthrough") {
+                transport = vintf::Transport::PASSTHROUGH;
+                switch (entry.arch) {
+                    case lshal::ARCH32:
+                        arch = vintf::Arch::ARCH_32;    break;
+                    case lshal::ARCH64:
+                        arch = vintf::Arch::ARCH_64;    break;
+                    case lshal::ARCH_BOTH:
+                        arch = vintf::Arch::ARCH_32_64; break;
+                    case lshal::ARCH_UNKNOWN: // fallthrough
+                    default:
+                        mErr << "Warning: '" << fqName.package()
+                             << "' doesn't have bitness info, assuming 32+64." << std::endl;
+                        arch = vintf::Arch::ARCH_32_64;
+                }
+            } else {
+                mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+                continue;
+            }
+
+            bool done = false;
+            for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
+                if (hal->transport() != transport) {
+                    if (transport != vintf::Transport::PASSTHROUGH) {
+                        mErr << "Fatal: should not reach here. Generated result may be wrong."
+                             << std::endl;
+                    }
+                    done = true;
+                    break;
+                }
+                if (hal->hasVersion(version)) {
+                    if (&table != &mImplementationsTable) {
+                        hal->interfaces[interfaceName].name = interfaceName;
+                        hal->interfaces[interfaceName].instances.insert(instanceName);
+                    }
+                    done = true;
+                    break;
+                }
+            }
+            if (done) {
+                continue; // to next TableEntry
+            }
+            decltype(vintf::ManifestHal::interfaces) interfaces;
+            if (&table != &mImplementationsTable) {
+                interfaces[interfaceName].name = interfaceName;
+                interfaces[interfaceName].instances.insert(instanceName);
+            }
+            if (!manifest.add(vintf::ManifestHal{
+                    .format = vintf::HalFormat::HIDL,
+                    .name = fqName.package(),
+                    .versions = {version},
+                    .transportArch = {transport, arch},
+                    .interfaces = interfaces})) {
+                mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+            }
+        }
+    });
+    mOut << vintf::gHalManifestConverter(manifest);
+}
+
+static const std::string &getArchString(Architecture arch) {
+    static const std::string sStr64 = "64";
+    static const std::string sStr32 = "32";
+    static const std::string sStrBoth = "32+64";
+    static const std::string sStrUnknown = "";
+    switch (arch) {
+        case ARCH64:
+            return sStr64;
+        case ARCH32:
+            return sStr32;
+        case ARCH_BOTH:
+            return sStrBoth;
+        case ARCH_UNKNOWN: // fall through
+        default:
+            return sStrUnknown;
+    }
+}
+
+static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
+    switch (a) {
+        case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
+            return ARCH64;
+        case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
+            return ARCH32;
+        case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
+        default:
+            return ARCH_UNKNOWN;
+    }
+}
+
+void ListCommand::dumpTable() {
+    mServicesTable.description =
+            "All binderized services (registered services through hwservicemanager)";
+    mPassthroughRefTable.description =
+            "All interfaces that getService() has ever return as a passthrough interface;\n"
+            "PIDs / processes shown below might be inaccurate because the process\n"
+            "might have relinquished the interface or might have died.\n"
+            "The Server / Server CMD column can be ignored.\n"
+            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+            "the library and successfully fetched the passthrough implementation.";
+    mImplementationsTable.description =
+            "All available passthrough implementations (all -impl.so files)";
+    forEachTable([this] (const Table &table) {
+        mOut << table.description << std::endl;
+        mOut << std::left;
+        printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
+                  "PTR", "Clients", "Clients CMD");
+
+        for (const auto &entry : table) {
+            printLine(entry.interfaceName,
+                    entry.transport,
+                    getArchString(entry.arch),
+                    entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
+                    entry.serverCmdline,
+                    entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
+                    join(entry.clientPids, " "),
+                    join(entry.clientCmdlines, ";"));
+
+            // We're only interested in dumping debug info for already
+            // instantiated services. There's little value in dumping the
+            // debug info for a service we create on the fly, so we only operate
+            // on the "mServicesTable".
+            if (mEmitDebugInfo && &table == &mServicesTable) {
+                auto pair = splitFirst(entry.interfaceName, '/');
+                mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
+                        NullableOStream<std::ostream>(nullptr));
+            }
+        }
+        mOut << std::endl;
+    });
+
+}
+
+void ListCommand::dump() {
+    if (mVintf) {
+        dumpVintf();
+        if (!!mFileOutput) {
+            mFileOutput.buf().close();
+            delete &mFileOutput.buf();
+            mFileOutput = nullptr;
+        }
+        mOut = std::cout;
+    } else {
+        dumpTable();
+    }
+}
+
+void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
+    Table *table = nullptr;
+    switch (source) {
+        case HWSERVICEMANAGER_LIST :
+            table = &mServicesTable; break;
+        case PTSERVICEMANAGER_REG_CLIENT :
+            table = &mPassthroughRefTable; break;
+        case LIST_DLLIB :
+            table = &mImplementationsTable; break;
+        default:
+            mErr << "Error: Unknown source of entry " << source << std::endl;
+    }
+    if (table) {
+        table->entries.push_back(std::forward<TableEntry>(entry));
+    }
+}
+
+Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
+    using namespace ::android::hardware;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+        std::map<std::string, TableEntry> entries;
+        for (const auto &info : infos) {
+            std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
+                    std::string{info.instanceName.c_str()};
+            entries.emplace(interfaceName, TableEntry{
+                .interfaceName = interfaceName,
+                .transport = "passthrough",
+                .serverPid = NO_PID,
+                .serverObjectAddress = NO_PTR,
+                .clientPids = {},
+                .arch = ARCH_UNKNOWN
+            }).first->second.arch |= fromBaseArchitecture(info.arch);
+        }
+        for (auto &&pair : entries) {
+            putEntry(LIST_DLLIB, std::move(pair.second));
+        }
+    });
+    if (!ret.isOk()) {
+        mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
+             << ret.description() << std::endl;
+        return DUMP_ALL_LIBS_ERROR;
+    }
+    return OK;
+}
+
+Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
+    using namespace ::android::hardware;
+    using namespace ::android::hardware::details;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+        for (const auto &info : infos) {
+            if (info.clientPids.size() <= 0) {
+                continue;
+            }
+            putEntry(PTSERVICEMANAGER_REG_CLIENT, {
+                .interfaceName =
+                        std::string{info.interfaceName.c_str()} + "/" +
+                        std::string{info.instanceName.c_str()},
+                .transport = "passthrough",
+                .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
+                .serverObjectAddress = NO_PTR,
+                .clientPids = info.clientPids,
+                .arch = fromBaseArchitecture(info.arch)
+            });
+        }
+    });
+    if (!ret.isOk()) {
+        mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
+             << ret.description() << std::endl;
+        return DUMP_PASSTHROUGH_ERROR;
+    }
+    return OK;
+}
+
+Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
+    using namespace ::std;
+    using namespace ::android::hardware;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    const std::string mode = "hwbinder";
+
+    hidl_vec<hidl_string> fqInstanceNames;
+    // copying out for timeoutIPC
+    auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
+        fqInstanceNames = names;
+    });
+    if (!listRet.isOk()) {
+        mErr << "Error: Failed to list services for " << mode << ": "
+             << listRet.description() << std::endl;
+        return DUMP_BINDERIZED_ERROR;
+    }
+
+    Status status = OK;
+    // server pid, .ptr value of binder object, child pids
+    std::map<std::string, DebugInfo> allDebugInfos;
+    std::map<pid_t, std::map<uint64_t, Pids>> allPids;
+    for (const auto &fqInstanceName : fqInstanceNames) {
+        const auto pair = splitFirst(fqInstanceName, '/');
+        const auto &serviceName = pair.first;
+        const auto &instanceName = pair.second;
+        auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+        if (!getRet.isOk()) {
+            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                 << "cannot be fetched from service manager:"
+                 << getRet.description() << std::endl;
+            status |= DUMP_BINDERIZED_ERROR;
+            continue;
+        }
+        sp<IBase> service = getRet;
+        if (service == nullptr) {
+            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                 << "cannot be fetched from service manager (null)"
+                 << std::endl;
+            status |= DUMP_BINDERIZED_ERROR;
+            continue;
+        }
+        auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
+            allDebugInfos[fqInstanceName] = debugInfo;
+            if (debugInfo.pid >= 0) {
+                allPids[static_cast<pid_t>(debugInfo.pid)].clear();
+            }
+        });
+        if (!debugRet.isOk()) {
+            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                 << "debugging information cannot be retrieved:"
+                 << debugRet.description() << std::endl;
+            status |= DUMP_BINDERIZED_ERROR;
+        }
+    }
+    for (auto &pair : allPids) {
+        pid_t serverPid = pair.first;
+        if (!getReferencedPids(serverPid, &allPids[serverPid])) {
+            mErr << "Warning: no information for PID " << serverPid
+                      << ", are you root?" << std::endl;
+            status |= DUMP_BINDERIZED_ERROR;
+        }
+    }
+    for (const auto &fqInstanceName : fqInstanceNames) {
+        auto it = allDebugInfos.find(fqInstanceName);
+        if (it == allDebugInfos.end()) {
+            putEntry(HWSERVICEMANAGER_LIST, {
+                .interfaceName = fqInstanceName,
+                .transport = mode,
+                .serverPid = NO_PID,
+                .serverObjectAddress = NO_PTR,
+                .clientPids = {},
+                .arch = ARCH_UNKNOWN
+            });
+            continue;
+        }
+        const DebugInfo &info = it->second;
+        putEntry(HWSERVICEMANAGER_LIST, {
+            .interfaceName = fqInstanceName,
+            .transport = mode,
+            .serverPid = info.pid,
+            .serverObjectAddress = info.ptr,
+            .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
+                    ? Pids{} : allPids[info.pid][info.ptr],
+            .arch = fromBaseArchitecture(info.arch),
+        });
+    }
+    return status;
+}
+
+Status ListCommand::fetch() {
+    Status status = OK;
+    auto bManager = mLshal.serviceManager();
+    if (bManager == nullptr) {
+        mErr << "Failed to get defaultServiceManager()!" << std::endl;
+        status |= NO_BINDERIZED_MANAGER;
+    } else {
+        status |= fetchBinderized(bManager);
+        // Passthrough PIDs are registered to the binderized manager as well.
+        status |= fetchPassthrough(bManager);
+    }
+
+    auto pManager = mLshal.passthroughManager();
+    if (pManager == nullptr) {
+        mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
+        status |= NO_PASSTHROUGH_MANAGER;
+    } else {
+        status |= fetchAllLibraries(pManager);
+    }
+    return status;
+}
+
+Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
+    static struct option longOptions[] = {
+        // long options with short alternatives
+        {"help",      no_argument,       0, 'h' },
+        {"interface", no_argument,       0, 'i' },
+        {"transport", no_argument,       0, 't' },
+        {"arch",      no_argument,       0, 'r' },
+        {"pid",       no_argument,       0, 'p' },
+        {"address",   no_argument,       0, 'a' },
+        {"clients",   no_argument,       0, 'c' },
+        {"cmdline",   no_argument,       0, 'm' },
+        {"debug",     optional_argument, 0, 'd' },
+
+        // long options without short alternatives
+        {"sort",      required_argument, 0, 's' },
+        {"init-vintf",optional_argument, 0, 'v' },
+        { 0,          0,                 0,  0  }
+    };
+
+    int optionIndex;
+    int c;
+    // Lshal::parseArgs has set optind to the next option to parse
+    for (;;) {
+        // using getopt_long in case we want to add other options in the future
+        c = getopt_long(arg.argc, arg.argv,
+                "hitrpacmd", longOptions, &optionIndex);
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case 's': {
+            if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
+                mSortColumn = TableEntry::sortByInterfaceName;
+            } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
+                mSortColumn = TableEntry::sortByServerPid;
+            } else {
+                mErr << "Unrecognized sorting column: " << optarg << std::endl;
+                mLshal.usage(command);
+                return USAGE;
+            }
+            break;
+        }
+        case 'v': {
+            if (optarg) {
+                mFileOutput = new std::ofstream{optarg};
+                mOut = mFileOutput;
+                if (!mFileOutput.buf().is_open()) {
+                    mErr << "Could not open file '" << optarg << "'." << std::endl;
+                    return IO_ERROR;
+                }
+            }
+            mVintf = true;
+        }
+        case 'i': {
+            mSelectedColumns |= ENABLE_INTERFACE_NAME;
+            break;
+        }
+        case 't': {
+            mSelectedColumns |= ENABLE_TRANSPORT;
+            break;
+        }
+        case 'r': {
+            mSelectedColumns |= ENABLE_ARCH;
+            break;
+        }
+        case 'p': {
+            mSelectedColumns |= ENABLE_SERVER_PID;
+            break;
+        }
+        case 'a': {
+            mSelectedColumns |= ENABLE_SERVER_ADDR;
+            break;
+        }
+        case 'c': {
+            mSelectedColumns |= ENABLE_CLIENT_PIDS;
+            break;
+        }
+        case 'm': {
+            mEnableCmdlines = true;
+            break;
+        }
+        case 'd': {
+            mEmitDebugInfo = true;
+
+            if (optarg) {
+                mFileOutput = new std::ofstream{optarg};
+                mOut = mFileOutput;
+                if (!mFileOutput.buf().is_open()) {
+                    mErr << "Could not open file '" << optarg << "'." << std::endl;
+                    return IO_ERROR;
+                }
+                chown(optarg, AID_SHELL, AID_SHELL);
+            }
+            break;
+        }
+        case 'h': // falls through
+        default: // see unrecognized options
+            mLshal.usage(command);
+            return USAGE;
+        }
+    }
+    if (optind < arg.argc) {
+        // see non option
+        mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+    }
+
+    if (mSelectedColumns == 0) {
+        mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
+    }
+    return OK;
+}
+
+Status ListCommand::main(const std::string &command, const Arg &arg) {
+    Status status = parseArgs(command, arg);
+    if (status != OK) {
+        return status;
+    }
+    status = fetch();
+    postprocess();
+    dump();
+    return status;
+}
+
+}  // namespace lshal
+}  // namespace android
+
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
new file mode 100644
index 0000000..42c965f
--- /dev/null
+++ b/cmds/lshal/ListCommand.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
+
+#include <stdint.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include "NullableOStream.h"
+#include "TableEntry.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class ListCommand {
+public:
+    ListCommand(Lshal &lshal);
+    Status main(const std::string &command, const Arg &arg);
+private:
+    Status parseArgs(const std::string &command, const Arg &arg);
+    Status fetch();
+    void postprocess();
+    void dump();
+    void putEntry(TableEntrySource source, TableEntry &&entry);
+    Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    bool getReferencedPids(
+        pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
+    void dumpTable();
+    void dumpVintf() const;
+    void printLine(
+            const std::string &interfaceName,
+            const std::string &transport,
+            const std::string &arch,
+            const std::string &server,
+            const std::string &serverCmdline,
+            const std::string &address, const std::string &clients,
+            const std::string &clientCmdlines) const ;
+    // Return /proc/{pid}/cmdline if it exists, else empty string.
+    const std::string &getCmdline(pid_t pid);
+    // Call getCmdline on all pid in pids. If it returns empty string, the process might
+    // have died, and the pid is removed from pids.
+    void removeDeadProcesses(Pids *pids);
+    void forEachTable(const std::function<void(Table &)> &f);
+    void forEachTable(const std::function<void(const Table &)> &f) const;
+
+    Lshal &mLshal;
+
+    Table mServicesTable{};
+    Table mPassthroughRefTable{};
+    Table mImplementationsTable{};
+
+    NullableOStream<std::ostream> mErr;
+    NullableOStream<std::ostream> mOut;
+    NullableOStream<std::ofstream> mFileOutput = nullptr;
+    TableEntryCompare mSortColumn = nullptr;
+    TableEntrySelect mSelectedColumns = 0;
+    // If true, cmdlines will be printed instead of pid.
+    bool mEnableCmdlines = false;
+
+    // If true, calls IBase::debug(...) on each service.
+    bool mEmitDebugInfo = false;
+
+    bool mVintf = false;
+    // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it.
+    // If an entry exist but is an empty string, process might have died.
+    // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
+    std::map<pid_t, std::string> mCmdlines;
+
+    DISALLOW_COPY_AND_ASSIGN(ListCommand);
+};
+
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
new file mode 100644
index 0000000..9db42f1
--- /dev/null
+++ b/cmds/lshal/Lshal.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "lshal"
+#include <android-base/logging.h>
+
+#include "Lshal.h"
+
+#include <set>
+#include <string>
+
+#include <hidl/ServiceManagement.h>
+
+#include "DebugCommand.h"
+#include "ListCommand.h"
+#include "PipeRelay.h"
+
+namespace android {
+namespace lshal {
+
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+Lshal::Lshal()
+    : mOut(std::cout), mErr(std::cerr),
+      mServiceManager(::android::hardware::defaultServiceManager()),
+      mPassthroughManager(::android::hardware::getPassthroughServiceManager()) {
+}
+
+Lshal::Lshal(std::ostream &out, std::ostream &err,
+            sp<hidl::manager::V1_0::IServiceManager> serviceManager,
+            sp<hidl::manager::V1_0::IServiceManager> passthroughManager)
+    : mOut(out), mErr(err),
+      mServiceManager(serviceManager),
+      mPassthroughManager(passthroughManager) {
+
+}
+
+void Lshal::usage(const std::string &command) const {
+    static const std::string helpSummary =
+            "lshal: List and debug HALs.\n"
+            "\n"
+            "commands:\n"
+            "    help            Print help message\n"
+            "    list            list HALs\n"
+            "    debug           debug a specified HAL\n"
+            "\n"
+            "If no command is specified, `list` is the default.\n";
+
+    static const std::string list =
+            "list:\n"
+            "    lshal\n"
+            "    lshal list\n"
+            "        List all hals with default ordering and columns (`lshal list -ipc`)\n"
+            "    lshal list [-h|--help]\n"
+            "        -h, --help: Print help message for list (`lshal help list`)\n"
+            "    lshal [list] [--interface|-i] [--transport|-t] [-r|--arch]\n"
+            "            [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
+            "            [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
+            "            [--debug|-d[=<output file>]]\n"
+            "        -i, --interface: print the interface name column\n"
+            "        -n, --instance: print the instance name column\n"
+            "        -t, --transport: print the transport mode column\n"
+            "        -r, --arch: print if the HAL is in 64-bit or 32-bit\n"
+            "        -p, --pid: print the server PID, or server cmdline if -m is set\n"
+            "        -a, --address: print the server object address column\n"
+            "        -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
+            "        -m, --cmdline: print cmdline instead of PIDs\n"
+            "        -d[=<output file>], --debug[=<output file>]: emit debug info from \n"
+            "                IBase::debug with empty options\n"
+            "        --sort=i, --sort=interface: sort by interface name\n"
+            "        --sort=p, --sort=pid: sort by server pid\n"
+            "        --init-vintf=<output file>: form a skeleton HAL manifest to specified\n"
+            "                      file, or stdout if no file specified.\n";
+
+    static const std::string debug =
+            "debug:\n"
+            "    lshal debug <interface> [options [options [...]]] \n"
+            "        Print debug information of a specified interface.\n"
+            "        <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
+            "            If instance name is missing `default` is used.\n"
+            "        options: space separated options to IBase::debug.\n";
+
+    static const std::string help =
+            "help:\n"
+            "    lshal -h\n"
+            "    lshal --help\n"
+            "    lshal help\n"
+            "        Print this help message\n"
+            "    lshal help list\n"
+            "        Print help message for list\n"
+            "    lshal help debug\n"
+            "        Print help message for debug\n";
+
+    if (command == "list") {
+        mErr << list;
+        return;
+    }
+    if (command == "debug") {
+        mErr << debug;
+        return;
+    }
+
+    mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help;
+}
+
+// A unique_ptr type using a custom deleter function.
+template<typename T>
+using deleted_unique_ptr = std::unique_ptr<T, std::function<void(T *)> >;
+
+static hardware::hidl_vec<hardware::hidl_string> convert(const std::vector<std::string> &v) {
+    hardware::hidl_vec<hardware::hidl_string> hv;
+    hv.resize(v.size());
+    for (size_t i = 0; i < v.size(); ++i) {
+        hv[i].setToExternal(v[i].c_str(), v[i].size());
+    }
+    return hv;
+}
+
+Status Lshal::emitDebugInfo(
+        const std::string &interfaceName,
+        const std::string &instanceName,
+        const std::vector<std::string> &options,
+        std::ostream &out,
+        NullableOStream<std::ostream> err) const {
+    using android::hidl::base::V1_0::IBase;
+
+    hardware::Return<sp<IBase>> retBase = serviceManager()->get(interfaceName, instanceName);
+
+    if (!retBase.isOk()) {
+        std::string msg = "Cannot get " + interfaceName + "/" + instanceName + ": "
+                + retBase.description();
+        err << msg << std::endl;
+        LOG(ERROR) << msg;
+        return TRANSACTION_ERROR;
+    }
+
+    sp<IBase> base = retBase;
+    if (base == nullptr) {
+        std::string msg = interfaceName + "/" + instanceName + " does not exist, or "
+                + "no permission to connect.";
+        err << msg << std::endl;
+        LOG(ERROR) << msg;
+        return NO_INTERFACE;
+    }
+
+    PipeRelay relay(out);
+
+    if (relay.initCheck() != OK) {
+        std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
+        err << msg << std::endl;
+        LOG(ERROR) << msg;
+        return IO_ERROR;
+    }
+
+    deleted_unique_ptr<native_handle_t> fdHandle(
+        native_handle_create(1 /* numFds */, 0 /* numInts */),
+        native_handle_delete);
+
+    fdHandle->data[0] = relay.fd();
+
+    hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options));
+
+    if (!ret.isOk()) {
+        std::string msg = "debug() FAILED on " + interfaceName + "/" + instanceName + ": "
+                + ret.description();
+        err << msg << std::endl;
+        LOG(ERROR) << msg;
+        return TRANSACTION_ERROR;
+    }
+    return OK;
+}
+
+Status Lshal::parseArgs(const Arg &arg) {
+    static std::set<std::string> sAllCommands{"list", "debug", "help"};
+    optind = 1;
+    if (optind >= arg.argc) {
+        // no options at all.
+        return OK;
+    }
+    mCommand = arg.argv[optind];
+    if (sAllCommands.find(mCommand) != sAllCommands.end()) {
+        ++optind;
+        return OK; // mCommand is set correctly
+    }
+
+    if (mCommand.size() > 0 && mCommand[0] == '-') {
+        // first argument is an option, set command to "" (which is recognized as "list")
+        mCommand = "";
+        return OK;
+    }
+
+    mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+    usage();
+    return USAGE;
+}
+
+void signalHandler(int sig) {
+    if (sig == SIGINT) {
+        int retVal;
+        pthread_exit(&retVal);
+    }
+}
+
+Status Lshal::main(const Arg &arg) {
+    // Allow SIGINT to terminate all threads.
+    signal(SIGINT, signalHandler);
+
+    Status status = parseArgs(arg);
+    if (status != OK) {
+        return status;
+    }
+    if (mCommand == "help") {
+        usage(optind < arg.argc ? arg.argv[optind] : "");
+        return USAGE;
+    }
+    // Default command is list
+    if (mCommand == "list" || mCommand == "") {
+        return ListCommand{*this}.main(mCommand, arg);
+    }
+    if (mCommand == "debug") {
+        return DebugCommand{*this}.main(mCommand, arg);
+    }
+    usage();
+    return USAGE;
+}
+
+NullableOStream<std::ostream> Lshal::err() const {
+    return mErr;
+}
+NullableOStream<std::ostream> Lshal::out() const {
+    return mOut;
+}
+
+const sp<IServiceManager> &Lshal::serviceManager() const {
+    return mServiceManager;
+}
+
+const sp<IServiceManager> &Lshal::passthroughManager() const {
+    return mPassthroughManager;
+}
+
+}  // namespace lshal
+}  // namespace android
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
new file mode 100644
index 0000000..00db5d0
--- /dev/null
+++ b/cmds/lshal/Lshal.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
+
+#include <iostream>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <utils/StrongPointer.h>
+
+#include "NullableOStream.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal {
+public:
+    Lshal();
+    Lshal(std::ostream &out, std::ostream &err,
+            sp<hidl::manager::V1_0::IServiceManager> serviceManager,
+            sp<hidl::manager::V1_0::IServiceManager> passthroughManager);
+    Status main(const Arg &arg);
+    void usage(const std::string &command = "") const;
+    NullableOStream<std::ostream> err() const;
+    NullableOStream<std::ostream> out() const;
+    const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const;
+    const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const;
+
+    Status emitDebugInfo(
+            const std::string &interfaceName,
+            const std::string &instanceName,
+            const std::vector<std::string> &options,
+            std::ostream &out,
+            NullableOStream<std::ostream> err) const;
+private:
+    Status parseArgs(const Arg &arg);
+    std::string mCommand;
+    Arg mCmdArgs;
+    NullableOStream<std::ostream> mOut;
+    NullableOStream<std::ostream> mErr;
+
+    sp<hidl::manager::V1_0::IServiceManager> mServiceManager;
+    sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager;
+
+    DISALLOW_COPY_AND_ASSIGN(Lshal);
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
new file mode 100644
index 0000000..ab37a04
--- /dev/null
+++ b/cmds/lshal/NullableOStream.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+template<typename S>
+class NullableOStream {
+public:
+    NullableOStream(S &os) : mOs(&os) {}
+    NullableOStream(S *os) : mOs(os) {}
+    NullableOStream &operator=(S &os) {
+        mOs = &os;
+        return *this;
+    }
+    NullableOStream &operator=(S *os) {
+        mOs = os;
+        return *this;
+    }
+    template<typename Other>
+    NullableOStream &operator=(const NullableOStream<Other> &other) {
+        mOs = other.mOs;
+        return *this;
+    }
+
+    const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const {
+        if (mOs) {
+            (*mOs) << pf;
+        }
+        return *this;
+    }
+    template<typename T>
+    const NullableOStream &operator<<(const T &rhs) const {
+        if (mOs) {
+            (*mOs) << rhs;
+        }
+        return *this;
+    }
+    S& buf() const {
+        return *mOs;
+    }
+    operator bool() const {
+        return mOs != nullptr;
+    }
+private:
+    template<typename>
+    friend class NullableOStream;
+
+    S *mOs = nullptr;
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
new file mode 100644
index 0000000..54d19f6
--- /dev/null
+++ b/cmds/lshal/PipeRelay.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PipeRelay.h"
+
+#include <sys/socket.h>
+#include <utils/Thread.h>
+
+namespace android {
+namespace lshal {
+
+struct PipeRelay::RelayThread : public Thread {
+    explicit RelayThread(int fd, std::ostream &os);
+
+    bool threadLoop() override;
+
+private:
+    int mFd;
+    std::ostream &mOutStream;
+
+    DISALLOW_COPY_AND_ASSIGN(RelayThread);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
+    : mFd(fd),
+      mOutStream(os) {
+}
+
+bool PipeRelay::RelayThread::threadLoop() {
+    char buffer[1024];
+    ssize_t n = read(mFd, buffer, sizeof(buffer));
+
+    if (n <= 0) {
+        return false;
+    }
+
+    mOutStream.write(buffer, n);
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+PipeRelay::PipeRelay(std::ostream &os)
+    : mOutStream(os),
+      mInitCheck(NO_INIT) {
+    int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, mFds);
+
+    if (res < 0) {
+        mInitCheck = -errno;
+        return;
+    }
+
+    mThread = new RelayThread(mFds[0], os);
+    mInitCheck = mThread->run("RelayThread");
+}
+
+void PipeRelay::CloseFd(int *fd) {
+    if (*fd >= 0) {
+        close(*fd);
+        *fd = -1;
+    }
+}
+
+PipeRelay::~PipeRelay() {
+    if (mFds[1] >= 0) {
+        shutdown(mFds[1], SHUT_WR);
+    }
+
+    if (mFds[0] >= 0) {
+        shutdown(mFds[0], SHUT_RD);
+    }
+
+    if (mThread != NULL) {
+        mThread->join();
+        mThread.clear();
+    }
+
+    CloseFd(&mFds[1]);
+    CloseFd(&mFds[0]);
+}
+
+status_t PipeRelay::initCheck() const {
+    return mInitCheck;
+}
+
+int PipeRelay::fd() const {
+    return mFds[1];
+}
+
+}  // namespace lshal
+}  // namespace android
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
new file mode 100644
index 0000000..76b2b23
--- /dev/null
+++ b/cmds/lshal/PipeRelay.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
+#define FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
+#include <android-base/macros.h>
+#include <ostream>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace lshal {
+
+/* Creates an AF_UNIX socketpair and spawns a thread that relays any data
+ * written to the "write"-end of the pair to the specified output stream "os".
+ */
+struct PipeRelay {
+    explicit PipeRelay(std::ostream &os);
+    ~PipeRelay();
+
+    status_t initCheck() const;
+
+    // Returns the file descriptor corresponding to the "write"-end of the
+    // connection.
+    int fd() const;
+
+private:
+    struct RelayThread;
+
+    std::ostream &mOutStream;
+    status_t mInitCheck;
+    int mFds[2];
+    sp<RelayThread> mThread;
+
+    static void CloseFd(int *fd);
+
+    DISALLOW_COPY_AND_ASSIGN(PipeRelay);
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_
+
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
new file mode 100644
index 0000000..9ae8f78
--- /dev/null
+++ b/cmds/lshal/TableEntry.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+using Pids = std::vector<int32_t>;
+
+enum : unsigned int {
+    HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list()
+    PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient
+    LIST_DLLIB, // through listing dynamic libraries
+};
+using TableEntrySource = unsigned int;
+
+enum : unsigned int {
+    ARCH_UNKNOWN = 0,
+    ARCH32       = 1 << 0,
+    ARCH64       = 1 << 1,
+    ARCH_BOTH    = ARCH32 | ARCH64
+};
+using Architecture = unsigned int;
+
+struct TableEntry {
+    std::string interfaceName;
+    std::string transport;
+    int32_t serverPid;
+    std::string serverCmdline;
+    uint64_t serverObjectAddress;
+    Pids clientPids;
+    std::vector<std::string> clientCmdlines;
+    Architecture arch;
+
+    static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
+        return a.interfaceName < b.interfaceName;
+    };
+    static bool sortByServerPid(const TableEntry &a, const TableEntry &b) {
+        return a.serverPid < b.serverPid;
+    };
+};
+
+struct Table {
+    using Entries = std::vector<TableEntry>;
+    std::string description;
+    Entries entries;
+
+    Entries::iterator begin() { return entries.begin(); }
+    Entries::const_iterator begin() const { return entries.begin(); }
+    Entries::iterator end() { return entries.end(); }
+    Entries::const_iterator end() const { return entries.end(); }
+};
+
+using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
+
+enum : unsigned int {
+    ENABLE_INTERFACE_NAME = 1 << 0,
+    ENABLE_TRANSPORT      = 1 << 1,
+    ENABLE_SERVER_PID     = 1 << 2,
+    ENABLE_SERVER_ADDR    = 1 << 3,
+    ENABLE_CLIENT_PIDS    = 1 << 4,
+    ENABLE_ARCH           = 1 << 5
+};
+
+using TableEntrySelect = unsigned int;
+
+enum {
+    NO_PID = -1,
+    NO_PTR = 0
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
new file mode 100644
index 0000000..ca477bf
--- /dev/null
+++ b/cmds/lshal/Timeout.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <condition_variable>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <hidl/Status.h>
+
+namespace android {
+namespace lshal {
+
+static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
+
+class BackgroundTaskState {
+public:
+    BackgroundTaskState(std::function<void(void)> &&func)
+            : mFunc(std::forward<decltype(func)>(func)) {}
+    void notify() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mFinished = true;
+        lock.unlock();
+        mCondVar.notify_all();
+    }
+    template<class C, class D>
+    bool wait(std::chrono::time_point<C, D> end) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
+        return mFinished;
+    }
+    void operator()() {
+        mFunc();
+    }
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondVar;
+    bool mFinished = false;
+    std::function<void(void)> mFunc;
+};
+
+void *callAndNotify(void *data) {
+    BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
+    state();
+    state.notify();
+    return NULL;
+}
+
+template<class R, class P>
+bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
+    auto now = std::chrono::system_clock::now();
+    BackgroundTaskState state{std::forward<decltype(func)>(func)};
+    pthread_t thread;
+    if (pthread_create(&thread, NULL, callAndNotify, &state)) {
+        std::cerr << "FATAL: could not create background thread." << std::endl;
+        return false;
+    }
+    bool success = state.wait(now + delay);
+    if (!success) {
+        pthread_kill(thread, SIGINT);
+    }
+    pthread_join(thread, NULL);
+    return success;
+}
+
+template<class Function, class I, class... Args>
+typename std::result_of<Function(I *, Args...)>::type
+timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
+    using ::android::hardware::Status;
+    typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
+    auto boundFunc = std::bind(std::forward<Function>(func),
+            interfaceObject.get(), std::forward<Args>(args)...);
+    bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] {
+        ret = std::move(boundFunc());
+    });
+    if (!success) {
+        return Status::fromStatusT(TIMED_OUT);
+    }
+    return ret;
+}
+
+}  // namespace lshal
+}  // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
new file mode 100644
index 0000000..366c938
--- /dev/null
+++ b/cmds/lshal/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Lshal.h"
+
+int main(int argc, char **argv) {
+    using namespace ::android::lshal;
+    return Lshal{}.main(Arg{argc, argv});
+}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
new file mode 100644
index 0000000..972d508
--- /dev/null
+++ b/cmds/lshal/test.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Lshal"
+#include <android-base/logging.h>
+
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <android/hardware/tests/baz/1.0/IQuux.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Lshal.h"
+
+#define NELEMS(array)   static_cast<int>(sizeof(array) / sizeof(array[0]))
+
+using namespace testing;
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::hidl::manager::V1_0::IServiceNotification;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace hardware {
+namespace tests {
+namespace baz {
+namespace V1_0 {
+namespace implementation {
+struct Quux : android::hardware::tests::baz::V1_0::IQuux {
+    ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
+        const native_handle_t *handle = hh.getNativeHandle();
+        if (handle->numFds < 1) {
+            return Void();
+        }
+        int fd = handle->data[0];
+        std::string content{descriptor};
+        for (const auto &option : options) {
+            content += "\n";
+            content += option.c_str();
+        }
+        ssize_t written = write(fd, content.c_str(), content.size());
+        if (written != (ssize_t)content.size()) {
+            LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
+                    << content.size() << " bytes, errno = " << errno;
+        }
+        return Void();
+    }
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace baz
+} // namespace tests
+} // namespace hardware
+
+namespace lshal {
+
+
+class MockServiceManager : public IServiceManager {
+public:
+    template<typename T>
+    using R = ::android::hardware::Return<T>;
+    using String = const hidl_string&;
+    ~MockServiceManager() = default;
+
+#define MOCK_METHOD_CB(name) MOCK_METHOD1(name, R<void>(IServiceManager::name##_cb))
+
+    MOCK_METHOD2(get, R<sp<IBase>>(String, String));
+    MOCK_METHOD2(add, R<bool>(String, const sp<IBase>&));
+    MOCK_METHOD2(getTransport, R<IServiceManager::Transport>(String, String));
+    MOCK_METHOD_CB(list);
+    MOCK_METHOD2(listByInterface, R<void>(String, listByInterface_cb));
+    MOCK_METHOD3(registerForNotifications, R<bool>(String, String, const sp<IServiceNotification>&));
+    MOCK_METHOD_CB(debugDump);
+    MOCK_METHOD2(registerPassthroughClient, R<void>(String, String));
+    MOCK_METHOD_CB(interfaceChain);
+    MOCK_METHOD2(debug, R<void>(const hidl_handle&, const hidl_vec<hidl_string>&));
+    MOCK_METHOD_CB(interfaceDescriptor);
+    MOCK_METHOD_CB(getHashChain);
+    MOCK_METHOD0(setHalInstrumentation, R<void>());
+    MOCK_METHOD2(linkToDeath, R<bool>(const sp<hidl_death_recipient>&, uint64_t));
+    MOCK_METHOD0(ping, R<void>());
+    MOCK_METHOD_CB(getDebugInfo);
+    MOCK_METHOD0(notifySyspropsChanged, R<void>());
+    MOCK_METHOD1(unlinkToDeath, R<bool>(const sp<hidl_death_recipient>&));
+
+};
+
+class LshalTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        using ::android::hardware::tests::baz::V1_0::IQuux;
+        using ::android::hardware::tests::baz::V1_0::implementation::Quux;
+
+        err.str("");
+        out.str("");
+        serviceManager = new testing::NiceMock<MockServiceManager>();
+        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
+            [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
+                if (iface == IQuux::descriptor && inst == "default")
+                    return new Quux();
+                return nullptr;
+            }));
+    }
+    void TearDown() override {}
+
+    std::stringstream err;
+    std::stringstream out;
+    sp<MockServiceManager> serviceManager;
+};
+
+TEST_F(LshalTest, Debug) {
+    const char *args[] = {
+        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
+    };
+    EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
+            .main({NELEMS(args), const_cast<char **>(args)}));
+    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
+    EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(LshalTest, Debug2) {
+    const char *args[] = {
+        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
+    };
+    EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
+            .main({NELEMS(args), const_cast<char **>(args)}));
+    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
+    EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(LshalTest, Debug3) {
+    const char *args[] = {
+        "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
+    };
+    EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager)
+            .main({NELEMS(args), const_cast<char **>(args)}));
+    EXPECT_THAT(err.str(), HasSubstr("does not exist"));
+}
+
+} // namespace lshal
+} // namespace android
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleMock(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/cmds/lshal/utils.cpp b/cmds/lshal/utils.cpp
new file mode 100644
index 0000000..5550721
--- /dev/null
+++ b/cmds/lshal/utils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+std::string toHexString(uint64_t t) {
+    std::ostringstream os;
+    os << std::hex << std::setfill('0') << std::setw(16) << t;
+    return os.str();
+}
+
+std::vector<std::string> split(const std::string &s, char c) {
+    std::vector<std::string> components{};
+    size_t startPos = 0;
+    size_t matchPos;
+    while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+        components.push_back(s.substr(startPos, matchPos - startPos));
+        startPos = matchPos + 1;
+    }
+
+    if (startPos <= s.length()) {
+        components.push_back(s.substr(startPos));
+    }
+    return components;
+}
+
+void replaceAll(std::string *s, char from, char to) {
+    for (size_t i = 0; i < s->size(); ++i) {
+        if (s->at(i) == from) {
+            s->at(i) = to;
+        }
+    }
+}
+
+}  // namespace lshal
+}  // namespace android
+
diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h
new file mode 100644
index 0000000..45b922c
--- /dev/null
+++ b/cmds/lshal/utils.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
+
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace lshal {
+
+enum : unsigned int {
+    OK                                      = 0,
+    USAGE                                   = 1 << 0,
+    NO_BINDERIZED_MANAGER                   = 1 << 1,
+    NO_PASSTHROUGH_MANAGER                  = 1 << 2,
+    DUMP_BINDERIZED_ERROR                   = 1 << 3,
+    DUMP_PASSTHROUGH_ERROR                  = 1 << 4,
+    DUMP_ALL_LIBS_ERROR                     = 1 << 5,
+    IO_ERROR                                = 1 << 6,
+    NO_INTERFACE                            = 1 << 7,
+    TRANSACTION_ERROR                       = 1 << 8,
+};
+using Status = unsigned int;
+
+struct Arg {
+    int argc;
+    char **argv;
+};
+
+template <typename A>
+std::string join(const A &components, const std::string &separator) {
+    std::stringstream out;
+    bool first = true;
+    for (const auto &component : components) {
+        if (!first) {
+            out << separator;
+        }
+        out << component;
+
+        first = false;
+    }
+    return out.str();
+}
+
+std::string toHexString(uint64_t t);
+
+template<typename String>
+std::pair<String, String> splitFirst(const String &s, char c) {
+    const char *pos = strchr(s.c_str(), c);
+    if (pos == nullptr) {
+        return {s, {}};
+    }
+    return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
+}
+
+std::vector<std::string> split(const std::string &s, char c);
+
+void replaceAll(std::string *s, char from, char to);
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
new file mode 100644
index 0000000..b703ed4
--- /dev/null
+++ b/cmds/service/Android.bp
@@ -0,0 +1,26 @@
+cc_binary {
+    name: "service",
+
+    srcs: ["service.cpp"],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: ["-DXP_UNIX"],
+}
+
+cc_binary {
+    name: "vndservice",
+
+    proprietary: true,
+    srcs: ["service.cpp"],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: ["-DXP_UNIX", "-DVENDORSERVICES"],
+}
diff --git a/cmds/service/Android.mk b/cmds/service/Android.mk
deleted file mode 100644
index 275bbb2..0000000
--- a/cmds/service/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	service.cpp
-
-LOCAL_SHARED_LIBRARIES := libutils libbinder
-
-ifeq ($(TARGET_OS),linux)
-	LOCAL_CFLAGS += -DXP_UNIX
-	#LOCAL_SHARED_LIBRARIES += librt
-endif
-
-LOCAL_MODULE:= service
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 428b87c..bc11256 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -68,13 +68,6 @@
 
 int main(int argc, char* const argv[])
 {
-    sp<IServiceManager> sm = defaultServiceManager();
-    fflush(stdout);
-    if (sm == NULL) {
-        aerr << "service: Unable to get default service manager!" << endl;
-        return 20;
-    }
-    
     bool wantsUsage = false;
     int result = 0;
     
@@ -95,6 +88,15 @@
             break;
         }
     }
+#ifdef VENDORSERVICES
+    ProcessState::initWithDriver("/dev/vndbinder");
+#endif
+    sp<IServiceManager> sm = defaultServiceManager();
+    fflush(stdout);
+    if (sm == NULL) {
+        aerr << "service: Unable to get default service manager!" << endl;
+        return 20;
+    }
     
     if (optind >= argc) {
         wantsUsage = true;
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
new file mode 100644
index 0000000..39d92a7
--- /dev/null
+++ b/cmds/servicemanager/Android.bp
@@ -0,0 +1,52 @@
+cc_defaults {
+    name: "servicemanager_flags",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    product_variables: {
+        binder32bit: {
+            cflags: ["-DBINDER_IPC_32BIT=1"],
+        },
+    },
+
+    shared_libs: ["liblog"],
+}
+
+cc_binary {
+    name: "bctest",
+    defaults: ["servicemanager_flags"],
+    srcs: [
+        "bctest.c",
+        "binder.c",
+    ],
+}
+
+cc_binary {
+    name: "servicemanager",
+    defaults: ["servicemanager_flags"],
+    srcs: [
+        "service_manager.c",
+        "binder.c",
+    ],
+    shared_libs: ["libcutils", "libselinux"],
+    init_rc: ["servicemanager.rc"],
+}
+
+cc_binary {
+    name: "vndservicemanager",
+    defaults: ["servicemanager_flags"],
+    vendor: true,
+    srcs: [
+        "service_manager.c",
+        "binder.c",
+    ],
+    cflags: [
+        "-DVENDORSERVICEMANAGER=1",
+    ],
+    shared_libs: ["libcutils"],
+    static_libs: ["libselinux"],
+    init_rc: ["vndservicemanager.rc"],
+}
diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk
deleted file mode 100644
index b214f19..0000000
--- a/cmds/servicemanager/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-svc_c_flags =	\
-	-Wall -Wextra -Werror \
-
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-svc_c_flags += -DBINDER_IPC_32BIT=1
-endif
-endif
-
-include $(CLEAR_VARS)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_SRC_FILES := bctest.c binder.c
-LOCAL_CFLAGS += $(svc_c_flags)
-LOCAL_MODULE := bctest
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libselinux
-LOCAL_SRC_FILES := service_manager.c binder.c
-LOCAL_CFLAGS += $(svc_c_flags)
-LOCAL_MODULE := servicemanager
-LOCAL_INIT_RC := servicemanager.rc
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
index 6466654..354df67 100644
--- a/cmds/servicemanager/bctest.c
+++ b/cmds/servicemanager/bctest.c
@@ -62,7 +62,7 @@
     uint32_t svcmgr = BINDER_SERVICE_MANAGER;
     uint32_t handle;
 
-    bs = binder_open(128*1024);
+    bs = binder_open("/dev/binder", 128*1024);
     if (!bs) {
         fprintf(stderr, "failed to open binder driver\n");
         return -1;
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 27c461a..93a18fc 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -1,14 +1,18 @@
 /* Copyright 2008 The Android Open Source Project
  */
 
+#define LOG_TAG "Binder"
+
+#include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <sys/mman.h>
+#include <unistd.h>
+
+#include <log/log.h>
 
 #include "binder.h"
 
@@ -16,9 +20,6 @@
 
 #define TRACE 0
 
-#define LOG_TAG "Binder"
-#include <cutils/log.h>
-
 void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn);
 
 #if TRACE
@@ -93,7 +94,7 @@
     size_t mapsize;
 };
 
-struct binder_state *binder_open(size_t mapsize)
+struct binder_state *binder_open(const char* driver, size_t mapsize)
 {
     struct binder_state *bs;
     struct binder_version vers;
@@ -104,10 +105,10 @@
         return NULL;
     }
 
-    bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
     if (bs->fd < 0) {
-        fprintf(stderr,"binder: cannot open device (%s)\n",
-                strerror(errno));
+        fprintf(stderr,"binder: cannot open %s (%s)\n",
+                driver, strerror(errno));
         goto fail_open;
     }
 
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
index 7915fc2..c95b33f 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -5,7 +5,7 @@
 #define _BINDER_H_
 
 #include <sys/ioctl.h>
-#include <linux/binder.h>
+#include <linux/android/binder.h>
 
 struct binder_state;
 
@@ -46,7 +46,7 @@
                               struct binder_io *msg,
                               struct binder_io *reply);
 
-struct binder_state *binder_open(size_t mapsize);
+struct binder_state *binder_open(const char* driver, size_t mapsize);
 void binder_close(struct binder_state *bs);
 
 /* initiate a blocking binder call
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 68e3ceb..45bb1d0 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -17,13 +17,12 @@
 
 #include "binder.h"
 
-#if 0
-#define ALOGI(x...) fprintf(stderr, "svcmgr: " x)
-#define ALOGE(x...) fprintf(stderr, "svcmgr: " x)
+#ifdef VENDORSERVICEMANAGER
+#define LOG_TAG "VendorServiceManager"
 #else
 #define LOG_TAG "ServiceManager"
-#include <cutils/log.h>
 #endif
+#include <log/log.h>
 
 struct audit_data {
     pid_t pid;
@@ -60,7 +59,6 @@
     return 1;
 }
 
-static int selinux_enabled;
 static char *service_manager_context;
 static struct selabel_handle* sehandle;
 
@@ -89,10 +87,6 @@
 
 static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
 {
-    if (selinux_enabled <= 0) {
-        return true;
-    }
-
     return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
 }
 
@@ -101,10 +95,6 @@
     bool allowed;
     char *tctx = NULL;
 
-    if (selinux_enabled <= 0) {
-        return true;
-    }
-
     if (!sehandle) {
         ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
         abort();
@@ -369,13 +359,28 @@
     return 0;
 }
 
-int main()
+int main(int argc, char** argv)
 {
     struct binder_state *bs;
+    union selinux_callback cb;
+    char *driver;
 
-    bs = binder_open(128*1024);
+    if (argc > 1) {
+        driver = argv[1];
+    } else {
+        driver = "/dev/binder";
+    }
+
+    bs = binder_open(driver, 128*1024);
     if (!bs) {
-        ALOGE("failed to open binder driver\n");
+#ifdef VENDORSERVICEMANAGER
+        ALOGW("failed to open binder driver %s\n", driver);
+        while (true) {
+            sleep(UINT_MAX);
+        }
+#else
+        ALOGE("failed to open binder driver %s\n", driver);
+#endif
         return -1;
     }
 
@@ -384,28 +389,29 @@
         return -1;
     }
 
-    selinux_enabled = is_selinux_enabled();
-    sehandle = selinux_android_service_context_handle();
-    selinux_status_open(true);
-
-    if (selinux_enabled > 0) {
-        if (sehandle == NULL) {
-            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
-            abort();
-        }
-
-        if (getcon(&service_manager_context) != 0) {
-            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
-            abort();
-        }
-    }
-
-    union selinux_callback cb;
     cb.func_audit = audit_callback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
+#ifdef VENDORSERVICEMANAGER
+    sehandle = selinux_android_vendor_service_context_handle();
+#else
+    sehandle = selinux_android_service_context_handle();
+#endif
+    selinux_status_open(true);
+
+    if (sehandle == NULL) {
+        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
+        abort();
+    }
+
+    if (getcon(&service_manager_context) != 0) {
+        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
+        abort();
+    }
+
+
     binder_loop(bs, svcmgr_handler);
 
     return 0;
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index aee7bd8..aec211a 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -1,5 +1,5 @@
 service servicemanager /system/bin/servicemanager
-    class core
+    class core animation
     user system
     group system readproc
     critical
@@ -12,4 +12,3 @@
     onrestart restart drm
     onrestart restart cameraserver
     writepid /dev/cpuset/system-background/tasks
-
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
new file mode 100644
index 0000000..d5ddaaf
--- /dev/null
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -0,0 +1,6 @@
+service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder
+    class core
+    user system
+    group system readproc
+    writepid /dev/cpuset/system-background/tasks
+
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
new file mode 100644
index 0000000..3cf1148
--- /dev/null
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_MODULE := libtrace_proto
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
new file mode 100644
index 0000000..0bc08a9
--- /dev/null
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -0,0 +1,179 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message Trace {
+    repeated Increment increment = 1;
+}
+
+message Increment {
+    required int64 time_stamp = 1;
+
+    oneof increment {
+        Transaction        transaction          = 2;
+        SurfaceCreation    surface_creation     = 3;
+        SurfaceDeletion    surface_deletion     = 4;
+        BufferUpdate       buffer_update        = 5;
+        VSyncEvent         vsync_event          = 6;
+        DisplayCreation    display_creation     = 7;
+        DisplayDeletion    display_deletion     = 8;
+        PowerModeUpdate    power_mode_update    = 9;
+    }
+}
+
+message Transaction {
+    repeated SurfaceChange surface_change = 1;
+    repeated DisplayChange display_change = 2;
+
+    required bool synchronous = 3;
+    required bool animation   = 4;
+}
+
+message SurfaceChange {
+    required int32 id = 1;
+
+    oneof SurfaceChange {
+        PositionChange              position                = 2;
+        SizeChange                  size                    = 3;
+        AlphaChange                 alpha                   = 4;
+        LayerChange                 layer                   = 5;
+        CropChange                  crop                    = 6;
+        FinalCropChange             final_crop              = 7;
+        MatrixChange                matrix                  = 8;
+        OverrideScalingModeChange   override_scaling_mode   = 9;
+        TransparentRegionHintChange transparent_region_hint = 10;
+        LayerStackChange            layer_stack             = 11;
+        HiddenFlagChange            hidden_flag             = 12;
+        OpaqueFlagChange            opaque_flag             = 13;
+        SecureFlagChange            secure_flag             = 14;
+        DeferredTransactionChange   deferred_transaction    = 15;
+    }
+}
+
+message PositionChange {
+    required float x = 1;
+    required float y = 2;
+}
+
+message SizeChange {
+    required uint32 w = 1;
+    required uint32 h = 2;
+}
+
+message AlphaChange {
+    required float alpha = 1;
+}
+
+message LayerChange {
+    required uint32 layer = 1;
+}
+
+message CropChange {
+    required Rectangle rectangle = 1;
+}
+
+message FinalCropChange {
+    required Rectangle rectangle = 1;
+}
+
+message MatrixChange {
+    required float dsdx = 1;
+    required float dtdx = 2;
+    required float dsdy = 3;
+    required float dtdy = 4;
+}
+
+message OverrideScalingModeChange {
+    required int32 override_scaling_mode = 1;
+}
+
+message TransparentRegionHintChange {
+    repeated Rectangle region = 1;
+}
+
+message LayerStackChange {
+    required uint32 layer_stack = 1;
+}
+
+message HiddenFlagChange {
+    required bool hidden_flag = 1;
+}
+
+message OpaqueFlagChange {
+    required bool opaque_flag = 1;
+}
+
+message SecureFlagChange {
+    required bool secure_flag = 1;
+}
+
+message DeferredTransactionChange {
+    required int32  layer_id     = 1;
+    required uint64 frame_number = 2;
+}
+
+message DisplayChange {
+    required int32 id = 1;
+
+    oneof DisplayChange {
+        DispSurfaceChange surface     = 2;
+        LayerStackChange  layer_stack = 3;
+        SizeChange        size        = 4;
+        ProjectionChange  projection  = 5;
+    }
+}
+
+message DispSurfaceChange {
+    required uint64 buffer_queue_id   = 1;
+    required string buffer_queue_name = 2;
+}
+
+message ProjectionChange {
+    required int32     orientation = 1;
+    required Rectangle viewport    = 2;
+    required Rectangle frame       = 3;
+}
+
+message Rectangle {
+    required int32 left   = 1;
+    required int32 top    = 2;
+    required int32 right  = 3;
+    required int32 bottom = 4;
+}
+
+message SurfaceCreation {
+    required int32  id   = 1;
+    required string name = 2;
+    required uint32 w    = 3;
+    required uint32 h    = 4;
+}
+
+message SurfaceDeletion {
+    required int32 id = 1;
+}
+
+message BufferUpdate {
+    required int32  id           = 1;
+    required uint32 w            = 2;
+    required uint32 h            = 3;
+    required uint64 frame_number = 4;
+}
+
+message VSyncEvent {
+    required int64 when = 1;
+}
+
+message DisplayCreation {
+    required int32     id                = 1;
+    required string    name              = 2;
+    required int32     type              = 3;
+    required bool      is_secure         = 4;
+}
+
+message DisplayDeletion {
+    required int32 id = 1;
+}
+
+message PowerModeUpdate {
+    required int32  id   = 1;
+    required int32  mode = 2;
+}
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
new file mode 100644
index 0000000..1dd926c
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,75 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_TARGET_DIR := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call first-makefiles-under, /frameworks/native/cmds/surfacereplayer/proto)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+LOCAL_CPPFLAGS := -Wno-format
+
+LOCAL_MODULE := libsurfacereplayer
+
+LOCAL_SRC_FILES := \
+    BufferQueueScheduler.cpp \
+    Event.cpp \
+    Replayer.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libEGL \
+    libGLESv2 \
+    libbinder \
+    liblog \
+    libcutils \
+    libgui \
+    libui \
+    libutils \
+    libprotobuf-cpp-lite \
+    libbase \
+    libnativewindow \
+
+LOCAL_STATIC_LIBRARIES := \
+    libtrace_proto \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/..
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := surfacereplayer
+
+LOCAL_SRC_FILES := \
+    Main.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libprotobuf-cpp-lite \
+    libsurfacereplayer \
+    libutils \
+    libgui \
+
+LOCAL_STATIC_LIBRARIES := \
+    libtrace_proto \
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+
+LOCAL_MODULE_PATH := $(LOCAL_TARGET_DIR)
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
new file mode 100644
index 0000000..77de8dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "BufferQueueScheduler"
+
+#include "BufferQueueScheduler.h"
+
+#include <android/native_window.h>
+#include <gui/Surface.h>
+
+using namespace android;
+
+BufferQueueScheduler::BufferQueueScheduler(
+        const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
+      : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
+
+void BufferQueueScheduler::startScheduling() {
+    ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mSurfaceControl == nullptr) {
+        mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
+    }
+
+    while (mContinueScheduling) {
+        while (true) {
+            if (mBufferEvents.empty()) {
+                break;
+            }
+
+            BufferEvent event = mBufferEvents.front();
+            lock.unlock();
+
+            bufferUpdate(event.dimensions);
+            fillSurface(event.event);
+            mColor.modulate();
+            lock.lock();
+            mBufferEvents.pop();
+        }
+        mCondition.wait(lock);
+    }
+}
+
+void BufferQueueScheduler::addEvent(const BufferEvent& event) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mBufferEvents.push(event);
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::stopScheduling() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mContinueScheduling = false;
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::setSurfaceControl(
+        const sp<SurfaceControl>& surfaceControl, const HSV& color) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mSurfaceControl = surfaceControl;
+    mColor = color;
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
+    sp<Surface> s = mSurfaceControl->getSurface();
+    s->setBuffersDimensions(dimensions.width, dimensions.height);
+}
+
+void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
+    ANativeWindow_Buffer outBuffer;
+    sp<Surface> s = mSurfaceControl->getSurface();
+
+    status_t status = s->lock(&outBuffer, nullptr);
+
+    if (status != NO_ERROR) {
+        ALOGE("fillSurface: failed to lock buffer, (%d)", status);
+        return;
+    }
+
+    auto color = mColor.getRGB();
+
+    auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+    for (int y = 0; y < outBuffer.height; y++) {
+        for (int x = 0; x < outBuffer.width; x++) {
+            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+            pixel[0] = color.r;
+            pixel[1] = color.g;
+            pixel[2] = color.b;
+            pixel[3] = LAYER_ALPHA;
+        }
+    }
+
+    event->readyToExecute();
+
+    status = s->unlockAndPost();
+
+    ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
+}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
new file mode 100644
index 0000000..cb20fcc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+
+#include "Color.h"
+#include "Event.h"
+
+#include <gui/SurfaceControl.h>
+
+#include <utils/StrongPointer.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <utility>
+
+namespace android {
+
+auto constexpr LAYER_ALPHA = 190;
+
+struct Dimensions {
+    Dimensions() = default;
+    Dimensions(int w, int h) : width(w), height(h) {}
+
+    int width = 0;
+    int height = 0;
+};
+
+struct BufferEvent {
+    BufferEvent() = default;
+    BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
+
+    std::shared_ptr<Event> event;
+    Dimensions dimensions;
+};
+
+class BufferQueueScheduler {
+  public:
+    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
+
+    void startScheduling();
+    void addEvent(const BufferEvent&);
+    void stopScheduling();
+
+    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
+
+  private:
+    void bufferUpdate(const Dimensions& dimensions);
+
+    // Lock and fill the surface, block until the event is signaled by the main loop,
+    // then unlock and post the buffer.
+    void fillSurface(const std::shared_ptr<Event>& event);
+
+    sp<SurfaceControl> mSurfaceControl;
+    HSV mColor;
+    const int mSurfaceId;
+
+    bool mContinueScheduling;
+
+    std::queue<BufferEvent> mBufferEvents;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+};
+
+}  // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
new file mode 100644
index 0000000..ce644be
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Color.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
+#define ANDROID_SURFACEREPLAYER_COLOR_H
+
+#include <cmath>
+#include <cstdlib>
+
+namespace android {
+
+constexpr double modulateFactor = .0001;
+constexpr double modulateLimit = .80;
+
+struct RGB {
+    RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
+
+    uint8_t r = 0;
+    uint8_t g = 0;
+    uint8_t b = 0;
+};
+
+struct HSV {
+    HSV() = default;
+    HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
+
+    double h = 0;
+    double s = 0;
+    double v = 0;
+
+    RGB getRGB() const;
+
+    bool modulateUp = false;
+
+    void modulate();
+};
+
+void inline HSV::modulate() {
+    if(modulateUp) {
+        v += modulateFactor;
+    } else {
+        v -= modulateFactor;
+    }
+
+    if(v <= modulateLimit || v >= 1) {
+        modulateUp = !modulateUp;
+    }
+}
+
+inline RGB HSV::getRGB() const {
+    using namespace std;
+    double r = 0, g = 0, b = 0;
+
+    if (s == 0) {
+        r = v;
+        g = v;
+        b = v;
+    } else {
+        auto tempHue = static_cast<int>(h) % 360;
+        tempHue = tempHue / 60;
+
+        int i = static_cast<int>(trunc(tempHue));
+        double f = h - i;
+
+        double x = v * (1.0 - s);
+        double y = v * (1.0 - (s * f));
+        double z = v * (1.0 - (s * (1.0 - f)));
+
+        switch (i) {
+            case 0:
+                r = v;
+                g = z;
+                b = x;
+                break;
+
+            case 1:
+                r = y;
+                g = v;
+                b = x;
+                break;
+
+            case 2:
+                r = x;
+                g = v;
+                b = z;
+                break;
+
+            case 3:
+                r = x;
+                g = y;
+                b = v;
+                break;
+
+            case 4:
+                r = z;
+                g = x;
+                b = v;
+                break;
+
+            default:
+                r = v;
+                g = x;
+                b = y;
+                break;
+        }
+    }
+
+    return RGB(round(r * 255), round(g * 255), round(b * 255));
+}
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
new file mode 100644
index 0000000..390d398
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Event.h"
+
+using namespace android;
+
+Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
+
+void Event::readyToExecute() {
+    changeState(Event::EventState::Waiting);
+    waitUntil(Event::EventState::Signaled);
+    changeState(Event::EventState::Running);
+}
+
+void Event::complete() {
+    waitUntil(Event::EventState::Waiting);
+    changeState(Event::EventState::Signaled);
+    waitUntil(Event::EventState::Running);
+}
+
+void Event::waitUntil(Event::EventState state) {
+    std::unique_lock<std::mutex> lock(mLock);
+    mCond.wait(lock, [this, state] { return (mState == state); });
+}
+
+void Event::changeState(Event::EventState state) {
+    std::unique_lock<std::mutex> lock(mLock);
+    mState = state;
+    lock.unlock();
+
+    mCond.notify_one();
+}
+
+Increment::IncrementCase Event::getIncrementType() {
+    return mIncrementType;
+}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
new file mode 100644
index 0000000..44b60f5
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
+#define ANDROID_SURFACEREPLAYER_EVENT_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace android {
+
+class Event {
+  public:
+    Event(Increment::IncrementCase);
+
+    enum class EventState {
+        SettingUp,  // Completing as much time-independent work as possible
+        Waiting,    // Waiting for signal from main thread to finish execution
+        Signaled,   // Signaled by main thread, about to immediately switch to Running
+        Running     // Finishing execution of rest of work
+    };
+
+    void readyToExecute();
+    void complete();
+
+    Increment::IncrementCase getIncrementType();
+
+  private:
+    void waitUntil(EventState state);
+    void changeState(EventState state);
+
+    std::mutex mLock;
+    std::condition_variable mCond;
+
+    EventState mState = EventState::SettingUp;
+
+    Increment::IncrementCase mIncrementType;
+};
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
new file mode 100644
index 0000000..dd1dd7d
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Replayer - Main.cpp
+ *
+ * 1. Get flags from command line
+ * 2. Commit actions or settings based on the flags
+ * 3. Initalize a replayer object with the filename passed in
+ * 4. Replay
+ * 5. Exit successfully or print error statement
+ */
+
+#include <replayer/Replayer.h>
+
+#include <csignal>
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+
+void printHelpMenu() {
+    std::cout << "SurfaceReplayer options:\n";
+    std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
+    std::cout << "  File path must be absolute" << std::endl << std::endl;
+
+    std::cout << "  -m  Stops the replayer at the start of the trace and switches ";
+                 "to manual replay\n";
+
+    std::cout << "\n  -t [Number of Threads]  Specifies the number of threads to be used while "
+                 "replaying (default is " << android::DEFAULT_THREADS << ")\n";
+
+    std::cout << "\n  -s [Timestamp]  Specify at what timestamp should the replayer switch "
+                 "to manual replay\n";
+
+    std::cout << "  -n  Ignore timestamps and run through trace as fast as possible\n";
+
+    std::cout << "  -l  Indefinitely loop the replayer\n";
+
+    std::cout << "  -h  Display help menu\n";
+
+    std::cout << std::endl;
+}
+
+int main(int argc, char** argv) {
+    std::string filename;
+    bool loop = false;
+    bool wait = true;
+    bool pauseBeginning = false;
+    int numThreads = DEFAULT_THREADS;
+    long stopHere = -1;
+
+    int opt = 0;
+    while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
+        switch (opt) {
+            case 'm':
+                pauseBeginning = true;
+                break;
+            case 't':
+                numThreads = atoi(optarg);
+                break;
+            case 's':
+                stopHere = atol(optarg);
+                break;
+            case 'n':
+                wait = false;
+                break;
+            case 'l':
+                loop = true;
+                break;
+            case 'h':
+            case '?':
+                printHelpMenu();
+                exit(0);
+            default:
+                std::cerr << "Invalid argument...exiting" << std::endl;
+                printHelpMenu();
+                exit(0);
+        }
+    }
+
+    char** input = argv + optind;
+    if (input[0] == NULL) {
+        std::cerr << "No trace file provided...exiting" << std::endl;
+        abort();
+    }
+    filename.assign(input[0]);
+
+    status_t status = NO_ERROR;
+    do {
+        android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
+        status = r.replay();
+    } while(loop);
+
+    if (status == NO_ERROR) {
+        std::cout << "Successfully finished replaying trace" << std::endl;
+    } else {
+        std::cerr << "Trace replayer returned error: " << status << std::endl;
+    }
+
+    return 0;
+}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
new file mode 100644
index 0000000..893f0dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/README.md
@@ -0,0 +1,262 @@
+SurfaceReplayer Documentation
+===================
+
+[go/SurfaceReplayer](go/SurfaceReplayer)
+
+SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
+[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
+
+* Creation and deletion of surfaces/displays
+* Alterations to the surfaces/displays called Transactions
+* Buffer Updates to surfaces
+* VSync events
+
+At their specified times to be as close to the original trace.
+
+Usage
+--------
+
+###Creating a trace
+
+SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
+utilize it. To allow it to write to the device, run
+
+`setenforce 0`
+
+To start recording a trace, run
+
+`service call SurfaceFlinger 1020 i32 1`
+
+To stop recording, run
+
+`service call SurfaceFlinger 1020 i32 0`
+
+The default location for the trace is `/data/SurfaceTrace.dat`
+
+###Executable
+
+To replay a specific trace, execute
+
+`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
+
+inside the android shell. This will replay the full trace and then exit. Running this command
+outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
+off VSync injections if it interrupted in any way other than fully replaying the trace
+
+The replay will not fill surfaces with their contents during the capture. Rather they are given a
+random color which will be the same every time the trace is replayed. Surfaces modulate their color
+at buffer updates.
+
+**Options:**
+
+- -m    pause the replayer at the start of the trace for manual replay
+- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
+- -s [Timestamp] switches to manual replay at specified timestamp
+- -n    Ignore timestamps and run through trace as fast as possible
+- -l    Indefinitely loop the replayer
+- -h    displays help menu
+
+**Manual Replay:**
+When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
+by the user. Pressing CTRL-C again will exit the replayer.
+
+Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
+input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
+without inputting a command repeats the previous command.
+
+- n  - steps the replayer to the next VSync event
+- ni - steps the replayer to the next increment
+- c  - continues normal replaying
+- c [milliseconds] - continue until specified number of milliseconds have passed
+- s [timestamp]    - continue and stop at specified timestamp
+- l  - list out timestamp of current increment
+- h  - displays help menu
+
+###Shared Library
+
+To use the shared library include these shared libraries
+
+`libsurfacereplayer`
+`libprotobuf-cpp-full`
+`libutils`
+
+And the static library
+
+`libtrace_proto`
+
+Include the replayer header at the top of your file
+
+`#include <replayer/Replayer.h>`
+
+There are two constructors for the replayer
+
+`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
+`Replayer(Trace& trace, ... ditto ...)`
+
+The first constructor takes in the filepath where the trace is located and loads in the trace
+object internally.
+- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
+- numThreads - Number of worker threads the replayer will use.
+- wait - **False**: Replayer ignores waits in between increments
+- stopHere - Time stamp of where the replayer should run to then switch to manual replay
+
+The second constructor includes all of the same parameters but takes in a preloaded trace object.
+To use add
+
+`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
+
+To your file
+
+After initializing the Replayer call
+
+    replayer.replay();
+
+And the trace will start replaying. Once the trace is finished replaying, the function will return.
+The layers that are visible at the end of the trace will remain on screen until the program
+terminates.
+
+
+**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
+never executed. This can be fixed by executing
+
+`service call SurfaceFlinger 23 i32 0`
+
+in the android shell
+
+Code Breakdown
+-------------
+
+The Replayer is composed of 5 components.
+
+- The data format of the trace (Trace.proto)
+- The Replayer object (Replayer.cpp)
+- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
+- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
+- The Main executable (Main.cpp)
+
+### Traces
+
+Traces are represented as a protobuf message located in surfacereplayer/proto/src.
+
+**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
+**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
+
+ - Transaction
+ - SurfaceCreation
+ - SurfaceDeletion
+ - DisplayCreation
+ - DisplayDeleteion
+ - BufferUpdate
+ - VSyncEvent
+ - PowerModeUpdate
+
+**Transactions** contain whether the transaction was synchronous or animated and *repeated*
+**SurfaceChanges** and **DisplayChanges**
+
+- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
+position, alpha, hidden, size, etc.
+- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
+size, layer stack, projection, etc.
+
+**Surface/Display Creation** contain the id of the surface/display and the name of the
+surface/display
+
+**Surface/Display Deletion** contain the id of the surface/display to be deleted
+
+**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
+buffer, and the frame number.
+
+**VSyncEvents** contain when the VSync event has occurred.
+
+**PowerModeUpdates** contain the id of the display being updated and what mode it is being
+changed to.
+
+To output the contents of a trace in a readable format, execute
+
+`**aprotoc** --decode=Trace \
+-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
+$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
+ < **YourTraceFile.dat** > **YourOutputName.txt**`
+
+
+###Replayer
+
+Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
+amount of time until the increment should be executed, then executing the increment. The first
+increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
+goes from there.
+
+Increments from the trace are played asynchronously rather than one by one, being dispatched by
+the main thread, queued up in a thread pool and completed when the main thread deems they are
+ready to finish execution.
+
+When an increment is dispatched, it completes as much work as it can before it has to be
+synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
+(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
+object. The main thread holds a queue of these Event objects and completes the
+corresponding Event base on its time stamp. After completing an increment, the main thread will
+dispatch another increment and continue.
+
+The main thread's execution flow is outlined below
+
+    initReplay() //queue up the initial increments
+    while(!pendingIncrements.empty()) { //while increments remaining
+        event = pendingIncrement.pop();
+        wait(event.time_stamp(); //waitUntil it is time to complete this increment
+
+        event.complete() //signal to let event finish
+        if(increments remaing()) {
+            dispatchEvent() //queue up another increment
+        }
+    }
+
+A worker thread's flow looks like so
+
+    //dispatched!
+    Execute non-time sensitive work here
+    ...
+    event.readyToExecute() //time sensitive point...waiting for Main Thread
+    ...
+    Finish execution
+
+
+### Event
+
+An Event is a simple synchronization mechanism used to facilitate communication between the main
+and worker threads. Every time an increment is dispatched, an Event object is also created.
+
+An Event can be in 4 different states:
+
+- **SettingUp** - The worker is in the process of completing all non-time sensitive work
+- **Waiting** - The worker is waiting on the main thread to signal it.
+- **Signaled** - The worker has just been signaled by the main thread
+- **Running** - The worker is running again and finishing the rest of its work.
+
+When the main thread wants to finish the execution of a worker, the worker can either still be
+**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
+main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
+state once **Signaled**. This last step exists in order to communicate back to the main thread that
+the worker thread has actually started completing its execution, rather than being preempted right
+after signalling. Once this happens, the main thread schedules the next worker. This makes sure
+there is a constant amount of workers running at one time.
+
+This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
+worker and main thread respectively.
+
+### BufferQueueScheduler
+
+During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
+buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
+**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
+which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
+handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
+
+When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
+**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
+every time an Event is completed.
+
+### Main
+
+The main exectuable reads in the command line arguments. Creates the Replayer using those
+arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
+gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
new file mode 100644
index 0000000..35b63ec
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,702 @@
+/* Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceReplayer"
+
+#include "Replayer.h"
+
+#include <android/native_window.h>
+
+#include <android-base/file.h>
+
+#include <gui/BufferQueue.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cmath>
+#include <condition_variable>
+#include <cstdlib>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using namespace android;
+
+std::atomic_bool Replayer::sReplayingManually(false);
+
+Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
+        nsecs_t stopHere)
+      : mTrace(),
+        mLoaded(false),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
+    srand(RAND_COLOR_SEED);
+
+    std::string input;
+    if (!android::base::ReadFileToString(filename, &input, true)) {
+        std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
+        abort();
+    }
+
+    mLoaded = mTrace.ParseFromString(input);
+    if (!mLoaded) {
+        std::cerr << "Trace did not load." << std::endl;
+        abort();
+    }
+
+    mCurrentTime = mTrace.increment(0).time_stamp();
+
+    sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
+}
+
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+      : mTrace(t),
+        mLoaded(true),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
+    srand(RAND_COLOR_SEED);
+    mCurrentTime = mTrace.increment(0).time_stamp();
+
+    sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
+}
+
+status_t Replayer::replay() {
+    signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
+
+    ALOGV("There are %d increments.", mTrace.increment_size());
+
+    status_t status = loadSurfaceComposerClient();
+
+    if (status != NO_ERROR) {
+        ALOGE("Couldn't create SurfaceComposerClient (%d)", status);
+        return status;
+    }
+
+    SurfaceComposerClient::enableVSyncInjections(true);
+
+    initReplay();
+
+    ALOGV("Starting actual Replay!");
+    while (!mPendingIncrements.empty()) {
+        mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+        if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+            mHasStopped = true;
+            sReplayingManually.store(true);
+        }
+
+        waitForConsoleCommmand();
+
+        if (mWaitForTimeStamps) {
+            waitUntilTimestamp(mCurrentIncrement.time_stamp());
+        }
+
+        auto event = mPendingIncrements.front();
+        mPendingIncrements.pop();
+
+        event->complete();
+
+        if (event->getIncrementType() == Increment::kVsyncEvent) {
+            mWaitingForNextVSync = false;
+        }
+
+        if (mIncrementIndex + mNumThreads < mTrace.increment_size()) {
+            status = dispatchEvent(mIncrementIndex + mNumThreads);
+
+            if (status != NO_ERROR) {
+                SurfaceComposerClient::enableVSyncInjections(false);
+                return status;
+            }
+        }
+
+        mIncrementIndex++;
+        mCurrentTime = mCurrentIncrement.time_stamp();
+    }
+
+    SurfaceComposerClient::enableVSyncInjections(false);
+
+    return status;
+}
+
+status_t Replayer::initReplay() {
+    for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) {
+        status_t status = dispatchEvent(i);
+
+        if (status != NO_ERROR) {
+            ALOGE("Unable to dispatch event (%d)", status);
+            return status;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void Replayer::stopAutoReplayHandler(int /*signal*/) {
+    if (sReplayingManually) {
+        SurfaceComposerClient::enableVSyncInjections(false);
+        exit(0);
+    }
+
+    sReplayingManually.store(true);
+}
+
+std::vector<std::string> split(const std::string& s, const char delim) {
+    std::vector<std::string> elems;
+    std::stringstream ss(s);
+    std::string item;
+    while (getline(ss, item, delim)) {
+        elems.push_back(item);
+    }
+    return elems;
+}
+
+bool isNumber(const std::string& s) {
+    return !s.empty() &&
+           std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
+void Replayer::waitForConsoleCommmand() {
+    if (!sReplayingManually || mWaitingForNextVSync) {
+        return;
+    }
+
+    while (true) {
+        std::string input = "";
+        std::cout << "> ";
+        getline(std::cin, input);
+
+        if (input.empty()) {
+            input = mLastInput;
+        } else {
+            mLastInput = input;
+        }
+
+        if (mLastInput.empty()) {
+            continue;
+        }
+
+        std::vector<std::string> inputs = split(input, ' ');
+
+        if (inputs[0] == "n") {  // next vsync
+            mWaitingForNextVSync = true;
+            break;
+
+        } else if (inputs[0] == "ni") {  // next increment
+            break;
+
+        } else if (inputs[0] == "c") {  // continue
+            if (inputs.size() > 1 && isNumber(inputs[1])) {
+                long milliseconds = stoi(inputs[1]);
+                std::thread([&] {
+                    std::cout << "Started!" << std::endl;
+                    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+                    sReplayingManually.store(true);
+                    std::cout << "Should have stopped!" << std::endl;
+                }).detach();
+            }
+            sReplayingManually.store(false);
+            mWaitingForNextVSync = false;
+            break;
+
+        } else if (inputs[0] == "s") {  // stop at this timestamp
+            if (inputs.size() < 1) {
+                std::cout << "No time stamp given" << std::endl;
+                continue;
+            }
+            sReplayingManually.store(false);
+            mStopTimeStamp = stol(inputs[1]);
+            mHasStopped = false;
+            break;
+        } else if (inputs[0] == "l") {  // list
+            std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+            continue;
+        } else if (inputs[0] == "q") {  // quit
+            SurfaceComposerClient::enableVSyncInjections(false);
+            exit(0);
+
+        } else if (inputs[0] == "h") {  // help
+                                        // add help menu
+            std::cout << "Manual Replay options:\n";
+            std::cout << " n  - Go to next VSync\n";
+            std::cout << " ni - Go to next increment\n";
+            std::cout << " c  - Continue\n";
+            std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+            std::cout << " s [timestamp]    - Continue and stop at specified timestamp\n";
+            std::cout << " l  - List out timestamp of current increment\n";
+            std::cout << " h  - Display help menu\n";
+            std::cout << std::endl;
+            continue;
+        }
+
+        std::cout << "Invalid Command" << std::endl;
+    }
+}
+
+status_t Replayer::dispatchEvent(int index) {
+    auto increment = mTrace.increment(index);
+    std::shared_ptr<Event> event = std::make_shared<Event>(increment.increment_case());
+    mPendingIncrements.push(event);
+
+    status_t status = NO_ERROR;
+    switch (increment.increment_case()) {
+        case increment.kTransaction: {
+            std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
+        } break;
+        case increment.kSurfaceCreation: {
+            std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+                    .detach();
+        } break;
+        case increment.kSurfaceDeletion: {
+            std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+                    .detach();
+        } break;
+        case increment.kBufferUpdate: {
+            std::lock_guard<std::mutex> lock1(mLayerLock);
+            std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+
+            Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h());
+            BufferEvent bufferEvent(event, dimensions);
+
+            auto layerId = increment.buffer_update().id();
+            if (mBufferQueueSchedulers.count(layerId) == 0) {
+                mBufferQueueSchedulers[layerId] = std::make_shared<BufferQueueScheduler>(
+                        mLayers[layerId], mColors[layerId], layerId);
+                mBufferQueueSchedulers[layerId]->addEvent(bufferEvent);
+
+                std::thread(&BufferQueueScheduler::startScheduling,
+                        mBufferQueueSchedulers[increment.buffer_update().id()].get())
+                        .detach();
+            } else {
+                auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()];
+                bqs->addEvent(bufferEvent);
+            }
+        } break;
+        case increment.kVsyncEvent: {
+            std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
+        } break;
+        case increment.kDisplayCreation: {
+            std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+                    .detach();
+        } break;
+        case increment.kDisplayDeletion: {
+            std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+                    .detach();
+        } break;
+        case increment.kPowerModeUpdate: {
+            std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+                    .detach();
+        } break;
+        default:
+            ALOGE("Unknown Increment Type: %d", increment.increment_case());
+            status = BAD_VALUE;
+            break;
+    }
+
+    return status;
+}
+
+status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
+    ALOGV("Started Transaction");
+
+    SurfaceComposerClient::openGlobalTransaction();
+
+    status_t status = NO_ERROR;
+
+    status = doSurfaceTransaction(t.surface_change());
+    doDisplayTransaction(t.display_change());
+
+    if (t.animation()) {
+        SurfaceComposerClient::setAnimationTransaction();
+    }
+
+    event->readyToExecute();
+
+    SurfaceComposerClient::closeGlobalTransaction(t.synchronous());
+
+    ALOGV("Ended Transaction");
+
+    return status;
+}
+
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+    status_t status = NO_ERROR;
+
+    for (const SurfaceChange& change : surfaceChanges) {
+        std::unique_lock<std::mutex> lock(mLayerLock);
+        if (mLayers[change.id()] == nullptr) {
+            mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+        }
+
+        switch (change.SurfaceChange_case()) {
+            case SurfaceChange::SurfaceChangeCase::kPosition:
+                status = setPosition(change.id(), change.position());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSize:
+                status = setSize(change.id(), change.size());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kAlpha:
+                status = setAlpha(change.id(), change.alpha());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayer:
+                status = setLayer(change.id(), change.layer());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kCrop:
+                status = setCrop(change.id(), change.crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kMatrix:
+                status = setMatrix(change.id(), change.matrix());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+                status = setFinalCrop(change.id(), change.final_crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+                status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+                status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayerStack:
+                status = setLayerStack(change.id(), change.layer_stack());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+                status = setHiddenFlag(change.id(), change.hidden_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+                status = setOpaqueFlag(change.id(), change.opaque_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+                status = setSecureFlag(change.id(), change.secure_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+                waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+                status = setDeferredTransaction(change.id(), change.deferred_transaction());
+                break;
+            default:
+                status = NO_ERROR;
+                break;
+        }
+
+        if (status != NO_ERROR) {
+            ALOGE("SET TRANSACTION FAILED");
+            return status;
+        }
+    }
+    return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+    for (const DisplayChange& change : displayChanges) {
+        ALOGV("Doing display transaction");
+        std::unique_lock<std::mutex> lock(mDisplayLock);
+        if (mDisplays[change.id()] == nullptr) {
+            mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+        }
+
+        switch (change.DisplayChange_case()) {
+            case DisplayChange::DisplayChangeCase::kSurface:
+                setDisplaySurface(change.id(), change.surface());
+                break;
+            case DisplayChange::DisplayChangeCase::kLayerStack:
+                setDisplayLayerStack(change.id(), change.layer_stack());
+                break;
+            case DisplayChange::DisplayChangeCase::kSize:
+                setDisplaySize(change.id(), change.size());
+                break;
+            case DisplayChange::DisplayChangeCase::kProjection:
+                setDisplayProjection(change.id(), change.projection());
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
+    ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
+    return mLayers[id]->setPosition(pc.x(), pc.y());
+}
+
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
+    ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
+    return mLayers[id]->setSize(sc.w(), sc.h());
+}
+
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
+    ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
+    return mLayers[id]->setLayer(lc.layer());
+}
+
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
+    ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
+    return mLayers[id]->setAlpha(ac.alpha());
+}
+
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
+    ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+            cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+            cc.rectangle().bottom());
+
+    Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+            cc.rectangle().bottom());
+    return mLayers[id]->setCrop(r);
+}
+
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
+    ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+            fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+            fcc.rectangle().bottom());
+    Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+            fcc.rectangle().bottom());
+    return mLayers[id]->setFinalCrop(r);
+}
+
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
+    ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
+            mc.dtdx(), mc.dsdy(), mc.dtdy());
+    return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
+}
+
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
+    ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
+    return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
+}
+
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
+    ALOGV("Setting Transparent Region Hint");
+    Region re = Region();
+
+    for (auto r : trhc.region()) {
+        Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
+        re.merge(rect);
+    }
+
+    return mLayers[id]->setTransparentRegionHint(re);
+}
+
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
+    ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
+    return mLayers[id]->setLayerStack(lsc.layer_stack());
+}
+
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
+    ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
+    layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
+}
+
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
+    ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
+    layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
+}
+
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
+    ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
+    layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
+}
+
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
+    ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
+          "frame_number=%llu",
+            id, dtc.layer_id(), dtc.frame_number());
+    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+        ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
+        return BAD_VALUE;
+    }
+
+    auto handle = mLayers[dtc.layer_id()]->getHandle();
+
+    return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
+}
+
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+    sp<IGraphicBufferProducer> outProducer;
+    sp<IGraphicBufferConsumer> outConsumer;
+    BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+    SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+    SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+    SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+    Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+            pc.viewport().bottom());
+    Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+    SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+        const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
+    event->readyToExecute();
+
+    ALOGV("Creating Surface Control: ID: %d", create.id());
+    sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(
+            String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0);
+
+    if (surfaceControl == nullptr) {
+        ALOGE("CreateSurfaceControl: unable to create surface control");
+        return BAD_VALUE;
+    }
+
+    std::lock_guard<std::mutex> lock1(mLayerLock);
+    auto& layer = mLayers[create.id()];
+    layer = surfaceControl;
+
+    mColors[create.id()] = HSV(rand() % 360, 1, 1);
+
+    mLayerCond.notify_all();
+
+    std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+    if (mBufferQueueSchedulers.count(create.id()) != 0) {
+        mBufferQueueSchedulers[create.id()]->setSurfaceControl(
+                mLayers[create.id()], mColors[create.id()]);
+    }
+
+    return NO_ERROR;
+}
+
+status_t Replayer::deleteSurfaceControl(
+        const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
+    ALOGV("Deleting %d Surface Control", delete_.id());
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+
+    mLayersPendingRemoval.push_back(delete_.id());
+
+    const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
+    if (iterator != mBufferQueueSchedulers.end()) {
+        (*iterator).second->stopScheduling();
+    }
+
+    std::lock_guard<std::mutex> lock2(mLayerLock);
+    if (mLayers[delete_.id()] != nullptr) {
+        mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+    }
+
+    return NO_ERROR;
+}
+
+void Replayer::doDeleteSurfaceControls() {
+    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+    std::lock_guard<std::mutex> lock2(mLayerLock);
+    if (!mLayersPendingRemoval.empty()) {
+        for (int id : mLayersPendingRemoval) {
+            mLayers.erase(id);
+            mColors.erase(id);
+            mBufferQueueSchedulers.erase(id);
+        }
+        mLayersPendingRemoval.clear();
+    }
+}
+
+status_t Replayer::injectVSyncEvent(
+        const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
+    ALOGV("Injecting VSync Event");
+
+    doDeleteSurfaceControls();
+
+    event->readyToExecute();
+
+    SurfaceComposerClient::injectVSync(vSyncEvent.when());
+
+    return NO_ERROR;
+}
+
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+    ALOGV("Creating display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    sp<IBinder> display = SurfaceComposerClient::createDisplay(
+            String8(create.name().c_str()), create.is_secure());
+    mDisplays[create.id()] = display;
+
+    mDisplayCond.notify_all();
+
+    ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+    ALOGV("Delete display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+    mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+    ALOGV("Updating power mode");
+    event->readyToExecute();
+    SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
+void Replayer::waitUntilTimestamp(int64_t timestamp) {
+    ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
+    std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
+}
+
+void Replayer::waitUntilDeferredTransactionLayerExists(
+        const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
+    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+        mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
+    }
+}
+
+status_t Replayer::loadSurfaceComposerClient() {
+    mComposerClient = new SurfaceComposerClient;
+    return mComposerClient->initCheck();
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
new file mode 100644
index 0000000..f36c9fd
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_H
+#define ANDROID_SURFACEREPLAYER_H
+
+#include "BufferQueueScheduler.h"
+#include "Color.h"
+#include "Event.h"
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <stdatomic.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+
+const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
+const auto RAND_COLOR_SEED = 700;
+const auto DEFAULT_THREADS = 3;
+
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
+
+class Replayer {
+  public:
+    Replayer(const std::string& filename, bool replayManually = false,
+            int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+            bool wait = true, nsecs_t stopHere = -1);
+
+    status_t replay();
+
+  private:
+    status_t initReplay();
+
+    void waitForConsoleCommmand();
+    static void stopAutoReplayHandler(int signal);
+
+    status_t dispatchEvent(int index);
+
+    status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
+    status_t createSurfaceControl(const SurfaceCreation& create,
+            const std::shared_ptr<Event>& event);
+    status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+            const std::shared_ptr<Event>& event);
+    status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+    void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+    void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+    void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
+
+    status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+    void doDisplayTransaction(const DisplayChanges& displayChange);
+
+    status_t setPosition(layer_id id, const PositionChange& pc);
+    status_t setSize(layer_id id, const SizeChange& sc);
+    status_t setAlpha(layer_id id, const AlphaChange& ac);
+    status_t setLayer(layer_id id, const LayerChange& lc);
+    status_t setCrop(layer_id id, const CropChange& cc);
+    status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+    status_t setMatrix(layer_id id, const MatrixChange& mc);
+    status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+    status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+    status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+    status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+    status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+    status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+    status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+    void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+    void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+    void setDisplaySize(display_id id, const SizeChange& sc);
+    void setDisplayProjection(display_id id, const ProjectionChange& pc);
+
+    void doDeleteSurfaceControls();
+    void waitUntilTimestamp(int64_t timestamp);
+    void waitUntilDeferredTransactionLayerExists(
+            const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
+    status_t loadSurfaceComposerClient();
+
+    Trace mTrace;
+    bool mLoaded = false;
+    int32_t mIncrementIndex = 0;
+    int64_t mCurrentTime = 0;
+    int32_t mNumThreads = DEFAULT_THREADS;
+
+    Increment mCurrentIncrement;
+
+    std::string mLastInput;
+
+    static atomic_bool sReplayingManually;
+    bool mWaitingForNextVSync;
+    bool mWaitForTimeStamps;
+    nsecs_t mStopTimeStamp;
+    bool mHasStopped;
+
+    std::mutex mLayerLock;
+    std::condition_variable mLayerCond;
+    std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
+    std::unordered_map<layer_id, HSV> mColors;
+
+    std::mutex mPendingLayersLock;
+    std::vector<layer_id> mLayersPendingRemoval;
+
+    std::mutex mBufferQueueSchedulerLock;
+    std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
+
+    std::mutex mDisplayLock;
+    std::condition_variable mDisplayCond;
+    std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
+    sp<SurfaceComposerClient> mComposerClient;
+    std::queue<std::shared_ptr<Event>> mPendingIncrements;
+};
+
+}  // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
new file mode 100644
index 0000000..a892e46
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+from subprocess import call
+import os
+proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
+call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
+
+from trace_pb2 import *
+
+trace = Trace()
+
+def main():
+    global trace
+    while(1):
+        option = main_menu()
+
+        if option == 0:
+            break
+
+        increment = trace.increment.add()
+        increment.time_stamp  = int(input("Time stamp of action: "))
+
+        if option == 1:
+           transaction(increment)
+        elif option == 2:
+            surface_create(increment)
+        elif option == 3:
+            surface_delete(increment)
+        elif option == 4:
+            display_create(increment)
+        elif option == 5:
+            display_delete(increment)
+        elif option == 6:
+            buffer_update(increment)
+        elif option == 7:
+            vsync_event(increment)
+        elif option == 8:
+            power_mode_update(increment)
+
+    seralizeTrace()
+
+def seralizeTrace():
+    with open("trace.dat", 'wb') as f:
+        f.write(trace.SerializeToString())
+
+
+def main_menu():
+    print ("")
+    print ("What would you like to do?")
+    print ("1. Add transaction")
+    print ("2. Add surface creation")
+    print ("3. Add surface deletion")
+    print ("4. Add display creation")
+    print ("5. Add display deletion")
+    print ("6. Add buffer update")
+    print ("7. Add VSync event")
+    print ("8. Add power mode update")
+    print ("0. Finish and serialize")
+    print ("")
+
+    return int(input("> "))
+
+def transaction_menu():
+    print ("")
+    print ("What kind of transaction?")
+    print ("1. Position Change")
+    print ("2. Size Change")
+    print ("3. Alpha Change")
+    print ("4. Layer Change")
+    print ("5. Crop Change")
+    print ("6. Final Crop Change")
+    print ("7. Matrix Change")
+    print ("8. Override Scaling Mode Change")
+    print ("9. Transparent Region Hint Change")
+    print ("10. Layer Stack Change")
+    print ("11. Hidden Flag Change")
+    print ("12. Opaque Flag Change")
+    print ("13. Secure Flag Change")
+    print ("14. Deferred Transaction Change")
+    print ("15. Display - Surface Change")
+    print ("16. Display - Layer Stack Change")
+    print ("17. Display - Size Change")
+    print ("18. Display - Projection Change")
+    print ("0. Finished adding Changes to this transaction")
+    print ("")
+
+    return int(input("> "))
+
+def transaction(increment):
+    global trace
+
+    increment.transaction.synchronous \
+            = bool(input("Is transaction synchronous (True/False): "))
+    increment.transaction.animation \
+            = bool(input("Is transaction animated (True/False): "))
+
+    while(1):
+        option = transaction_menu()
+
+        if option == 0:
+            break
+
+        change = None
+        if option <= 14:
+            change = increment.transaction.surface_change.add()
+        elif option >= 15 and option <= 18:
+            change = increment.transaction.display_change.add()
+
+        change.id = int(input("ID of layer/display to undergo a change: "))
+
+        if option == 1:
+            change.position.x, change.position.y = position()
+        elif option == 2:
+            change.size.w, change.size.h = size()
+        elif option == 3:
+            change.alpha.alpha = alpha()
+        elif option == 4:
+            change.layer.layer = layer()
+        elif option == 5:
+            change.crop.rectangle.left,  change.crop.rectangle.top, \
+            change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
+        elif option == 6:
+            change.final_crop.rectangle.left, \
+            change.final_crop.rectangle.top,  \
+            change.final_crop.rectangle.right,\
+            change.final_crop.rectangle.bottom = final_crop()
+        elif option == 7:
+            change.matrix.dsdx,\
+            change.matrix.dtdx,\
+            change.matrix.dsdy,\
+            change.matrix.dtdy = layer()
+        elif option == 8:
+            change.override_scaling_mode.override_scaling_mode \
+                                     = override_scaling_mode()
+        elif option == 9:
+            for rect in transparent_region_hint():
+                new = increment.transparent_region_hint.region.add()
+                new.left = rect[0]
+                new.top = rect[1]
+                new.right = rect[2]
+                new.bottom = rect[3]
+        elif option == 10:
+            change.layer_stack.layer_stack = layer_stack()
+        elif option == 11:
+            change.hidden_flag.hidden_flag = hidden_flag()
+        elif option == 12:
+            change.opaque_flag.opaque_flag = opaque_flag()
+        elif option == 13:
+            change.secure_flag.secure_flag = secure_flag()
+        elif option == 14:
+            change.deferred_transaction.layer_id, \
+            change.deferred_transaction.frame_number = deferred_transaction()
+        elif option == 15:
+            change.surface.buffer_queue_id, \
+            change.surface.buffer_queue_name = surface()
+        elif option == 16:
+            change.layer_stack.layer_stack = layer_stack()
+        elif option == 17:
+            change.size.w, change.size.h = size()
+        elif option == 18:
+            projection(change)
+
+def surface_create(increment):
+    increment.surface_creation.id = int(input("Enter id: "))
+    n = str(raw_input("Enter name: "))
+    increment.surface_creation.name = n
+    increment.surface_creation.w = input("Enter w: ")
+    increment.surface_creation.h = input("Enter h: ")
+
+def surface_delete(increment):
+    increment.surface_deletion.id = int(input("Enter id: "))
+
+def display_create(increment):
+    increment.display_creation.id = int(input("Enter id: "))
+    increment.display_creation.name = str(raw_input("Enter name: "))
+    increment.display_creation.type = int(input("Enter type: "))
+    increment.display_creation.is_secure = bool(input("Enter if secure: "))
+
+def display_delete(increment):
+    increment.surface_deletion.id = int(input("Enter id: "))
+
+def buffer_update(increment):
+    increment.buffer_update.id = int(input("Enter id: "))
+    increment.buffer_update.w = int(input("Enter w: "))
+    increment.buffer_update.h = int(input("Enter h: "))
+    increment.buffer_update.frame_number = int(input("Enter frame_number: "))
+
+def vsync_event(increment):
+    increment.vsync_event.when = int(input("Enter when: "))
+
+def power_mode_update(increment):
+    increment.power_mode_update.id = int(input("Enter id: "))
+    increment.power_mode_update.mode = int(input("Enter mode: "))
+
+def position():
+    x = input("Enter x: ")
+    y = input("Enter y: ")
+
+    return float(x), float(y)
+
+def size():
+    w = input("Enter w: ")
+    h = input("Enter h: ")
+
+    return int(w), int(h)
+
+def alpha():
+    alpha = input("Enter alpha: ")
+
+    return float(alpha)
+
+def layer():
+    layer = input("Enter layer: ")
+
+    return int(layer)
+
+def crop():
+    return rectangle()
+
+def final_crop():
+    return rectangle()
+
+def matrix():
+    dsdx = input("Enter dsdx: ")
+    dtdx = input("Enter dtdx: ")
+    dsdy = input("Enter dsdy: ")
+    dtdy = input("Enter dtdy: ")
+
+    return float(dsdx)
+
+def override_scaling_mode():
+    mode = input("Enter override scaling mode: ")
+
+    return int(mode)
+
+def transparent_region_hint():
+    num = input("Enter number of rectangles in region: ")
+
+    return [rectangle() in range(x)]
+
+def layer_stack():
+    layer_stack = input("Enter layer stack: ")
+
+    return int(layer_stack)
+
+def hidden_flag():
+    flag = input("Enter hidden flag state (True/False): ")
+
+    return bool(flag)
+
+def opaque_flag():
+    flag = input("Enter opaque flag state (True/False): ")
+
+    return bool(flag)
+
+def secure_flag():
+    flag = input("Enter secure flag state (True/False): ")
+
+    return bool(flag)
+
+def deferred_transaction():
+    layer_id = input("Enter layer_id: ")
+    frame_number = input("Enter frame_number: ")
+
+    return int(layer_id), int(frame_number)
+
+def surface():
+    id = input("Enter id: ")
+    name = raw_input("Enter name: ")
+
+    return int(id), str(name)
+
+def projection(change):
+    change.projection.orientation = input("Enter orientation: ")
+    print("Enter rectangle for viewport")
+    change.projection.viewport.left, \
+    change.projection.viewport.top,  \
+    change.projection.viewport.right,\
+    change.projection.viewport.bottom = rectangle()
+    print("Enter rectangle for frame")
+    change.projection.frame.left, \
+    change.projection.frame.top,  \
+    change.projection.frame.right,\
+    change.projection.frame.bottom = rectangle()
+
+def rectangle():
+    left = input("Enter left: ")
+    top = input("Enter top: ")
+    right = input("Enter right: ")
+    bottom = input("Enter bottom: ")
+
+    return int(left), int(top), int(right), int(bottom)
+
+if __name__ == "__main__":
+    main()
diff --git a/cmds/vr/.clang-format b/cmds/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/cmds/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/data/etc/android.hardware.nfc.hce.xml b/data/etc/android.hardware.nfc.hce.xml
index 10b96b1..95da181 100644
--- a/data/etc/android.hardware.nfc.hce.xml
+++ b/data/etc/android.hardware.nfc.hce.xml
@@ -18,4 +18,5 @@
      NFC card emulation -->
 <permissions>
     <feature name="android.hardware.nfc.hce" />
+    <feature name="android.hardware.nfc.any" />
 </permissions>
diff --git a/data/etc/android.hardware.nfc.hcef.xml b/data/etc/android.hardware.nfc.hcef.xml
index 0d03023..b86890d 100644
--- a/data/etc/android.hardware.nfc.hcef.xml
+++ b/data/etc/android.hardware.nfc.hcef.xml
@@ -18,4 +18,5 @@
      NFC-F card emulation -->
 <permissions>
     <feature name="android.hardware.nfc.hcef" />
+    <feature name="android.hardware.nfc.any" />
 </permissions>
diff --git a/data/etc/android.hardware.nfc.xml b/data/etc/android.hardware.nfc.xml
index 81c4a84..5201fa2 100644
--- a/data/etc/android.hardware.nfc.xml
+++ b/data/etc/android.hardware.nfc.xml
@@ -18,4 +18,5 @@
      using Near-Field Communications (NFC). -->
 <permissions>
     <feature name="android.hardware.nfc" />
+    <feature name="android.hardware.nfc.any" />
 </permissions>
diff --git a/data/etc/android.hardware.telephony.carrierlock.xml b/data/etc/android.hardware.telephony.carrierlock.xml
new file mode 100644
index 0000000..50b1fe9
--- /dev/null
+++ b/data/etc/android.hardware.telephony.carrierlock.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices with telephony carrier restriction mechanism. -->
+<permissions>
+    <feature name="android.hardware.telephony.carrierlock" />
+</permissions>
diff --git a/data/etc/android.hardware.vr.headtracking-0.xml b/data/etc/android.hardware.vr.headtracking-0.xml
new file mode 100644
index 0000000..1b53995
--- /dev/null
+++ b/data/etc/android.hardware.vr.headtracking-0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the feature indicating that the device supports VR headtracking
+     level 0 -->
+<permissions>
+    <feature name="android.hardware.vr.headtracking" version="0" />
+</permissions>
diff --git a/data/etc/android.hardware.vr.headtracking-1.xml b/data/etc/android.hardware.vr.headtracking-1.xml
new file mode 100644
index 0000000..2ad8ccc
--- /dev/null
+++ b/data/etc/android.hardware.vr.headtracking-1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the feature indicating that the device supports VR headtracking
+     level 1 -->
+<permissions>
+    <feature name="android.hardware.vr.headtracking" version="1" />
+</permissions>
diff --git a/data/etc/android.hardware.vulkan.compute-0.xml b/data/etc/android.hardware.vulkan.compute-0.xml
new file mode 100644
index 0000000..bac2fde
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.compute-0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device supports Vulkan
+     compute level 0. -->
+<permissions>
+    <feature name="android.hardware.vulkan.compute" version="0" />
+</permissions>
diff --git a/data/etc/android.hardware.wifi.aware.xml b/data/etc/android.hardware.wifi.aware.xml
new file mode 100644
index 0000000..ae6272e
--- /dev/null
+++ b/data/etc/android.hardware.wifi.aware.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device includes WiFi Aware. -->
+<permissions>
+    <feature name="android.hardware.wifi.aware" />
+</permissions>
diff --git a/data/etc/android.hardware.wifi.nan.xml b/data/etc/android.hardware.wifi.nan.xml
deleted file mode 100644
index e557610..0000000
--- a/data/etc/android.hardware.wifi.nan.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi NAN. -->
-<permissions>
-    <feature name="android.hardware.wifi.nan" />
-</permissions>
diff --git a/data/etc/android.software.activities_on_secondary_displays.xml b/data/etc/android.software.activities_on_secondary_displays.xml
new file mode 100644
index 0000000..db1bdb5
--- /dev/null
+++ b/data/etc/android.software.activities_on_secondary_displays.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <feature name="android.software.activities_on_secondary_displays" />
+</permissions>
diff --git a/data/etc/android.software.autofill.xml b/data/etc/android.software.autofill.xml
new file mode 100644
index 0000000..c510d0c
--- /dev/null
+++ b/data/etc/android.software.autofill.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <feature name="android.software.autofill" />
+</permissions>
diff --git a/data/etc/android.software.companion_device_setup.xml b/data/etc/android.software.companion_device_setup.xml
new file mode 100644
index 0000000..e60ef88
--- /dev/null
+++ b/data/etc/android.software.companion_device_setup.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <feature name="android.software.companion_device_setup" />
+</permissions>
diff --git a/data/etc/android.software.cts.xml b/data/etc/android.software.cts.xml
new file mode 100644
index 0000000..0414c9a
--- /dev/null
+++ b/data/etc/android.software.cts.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+</permissions>
diff --git a/data/etc/android.software.preview_sdk.xml b/data/etc/android.software.preview_sdk.xml
new file mode 100644
index 0000000..928b4b3
--- /dev/null
+++ b/data/etc/android.software.preview_sdk.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <!-- The device is running a preview (i.e. unofficial) API version. -->
+    <feature name="android.software.preview_sdk" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index ab89ef5..835504f 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -23,6 +23,9 @@
      devices.
 -->
 <permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+
     <feature name="android.hardware.audio.output" />
     <feature name="android.hardware.location" />
     <feature name="android.hardware.location.network" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f9464e8..0d5d206 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -23,6 +23,9 @@
      devices.
 -->
 <permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+
     <feature name="android.hardware.audio.output" />
     <feature name="android.hardware.camera" />
     <feature name="android.hardware.location" />
@@ -42,7 +45,11 @@
     <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.activities_on_secondary_displays" />
     <feature name="android.software.print" />
+    <feature name="android.software.companion_device_setup" />
+    <feature name="android.software.autofill" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
@@ -55,6 +62,9 @@
     <!-- Devices with all optimizations required to be a "VR Ready" device that
          pass all CTS tests for this feature must include feature
          android.hardware.vr.high_performance -->
+    <!-- Devices that support VR headtracking features and pass all CDD
+         requirements may include
+         android.hardware.vr.headtracking -->
 
     <!-- devices with GPS must include android.hardware.location.gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8128165..9b88648 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -23,6 +23,9 @@
      devices.
 -->
 <permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+
     <feature name="android.hardware.audio.output" />
     <feature name="android.hardware.location" />
     <feature name="android.hardware.location.network" />
@@ -42,7 +45,11 @@
     <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.activities_on_secondary_displays" />
     <feature name="android.software.print" />
+    <feature name="android.software.companion_device_setup" />
+    <feature name="android.software.autofill" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 84230da..d7c3730 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -21,6 +21,9 @@
      Wearable devices include watches, glasses, backpacks, and sweaters.
 -->
 <permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+
     <feature name="android.hardware.location" />
     <!-- devices supporting compass/magnitometer sensor must include
 	 android.hardware.sensor.compass.xml -->
@@ -36,6 +39,9 @@
     <!-- device administration -->
     <feature name="android.software.device_admin" />
 
+    <!-- input management and third-party input method editors -->
+    <feature name="android.software.input_methods" />
+
     <!-- devices with GPS must include device/google/clockwork/gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
          android.hardware.camera.autofocus.xml or
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 3ea453f..bb0ca32 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -14,90 +14,90 @@
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file 
-# that follow. The default is UTF-8 which is also the encoding used for all 
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
-# iconv built into libc) for the transcoding. See 
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
 # http://www.gnu.org/software/libiconv for the list of possible encodings.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or sequence of words) that should 
-# identify the project. Note that if you do not use Doxywizard you need 
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
 # to put quotes around the project name if it contains spaces.
 
 PROJECT_NAME           = "NDK API"
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 
+PROJECT_NUMBER         =
 
-# Using the PROJECT_BRIEF tag one can provide an optional one line description 
-# for a project that appears at the top of each page and should give viewer 
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
 # a quick idea about the purpose of the project. Keep the description short.
 
 PROJECT_BRIEF          = ""
 
-# With the PROJECT_LOGO tag one can specify an logo or icon that is 
-# included in the documentation. The maximum height of the logo should not 
-# exceed 55 pixels and the maximum width should not exceed 200 pixels. 
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
 # Doxygen will copy the logo to the output directory.
 
 PROJECT_LOGO           = logo.png
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
 # where doxygen was started. If left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = 
+OUTPUT_DIRECTORY       =
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
 # otherwise cause performance problems for the file system.
 
 CREATE_SUBDIRS         = NO
 
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, 
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
 # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
-# include brief member descriptions after the members that are listed in 
-# the file and class documentation (similar to JavaDoc). 
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
 # Set to NO to disable this.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
 # "represents" "a" "an" "the"
 
 ABBREVIATE_BRIEF       = "The $name class" \
@@ -112,256 +112,256 @@
                          an \
                          the
 
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
 # description.
 
 ALWAYS_DETAILED_SEC    = NO
 
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
 # to NO the shortest path that makes the file name unique will be used.
 
 FULL_PATH_NAMES        = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip. Note that you specify absolute paths here, but also 
-# relative paths, which will be relative from the directory where doxygen is 
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
 # started.
 
-STRIP_FROM_PATH        = 
+STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
 # are normally passed to the compiler using the -I flag.
 
-STRIP_FROM_INC_PATH    = 
+STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful if your file system 
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
 # doesn't support long names like on DOS, Mac, or CD-ROM.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
 # (thus requiring an explicit @brief command for a brief description.)
 
 JAVADOC_AUTOBRIEF      = NO
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
-# interpret the first line (until the first dot) of a Qt-style 
-# comment as the brief description. If set to NO, the comments 
-# will behave just like regular Qt-style comments (thus requiring 
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
 # an explicit \brief command for a brief description.)
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
 # description. Set this tag to YES if you prefer the old behaviour instead.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
 # re-implements.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
 # be part of the file/class/namespace that contains it.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
 # Doxygen uses this value to replace tabs by spaces in code fragments.
 
 TAB_SIZE               = 4
 
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
-# put the command \sideeffect (or @sideeffect) in the documentation, which 
-# will result in a user-defined paragraph with heading "Side Effects:". 
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
 # You can put \n's in the value part of an alias to insert newlines.
 
-ALIASES                = 
+ALIASES                =
 
-# This tag can be used to specify a number of word-keyword mappings (TCL only). 
-# A mapping has the form "name=value". For example adding 
-# "class=itcl::class" will allow you to use the command class in the 
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
 # itcl::class meaning.
 
-TCL_SUBST              = 
+TCL_SUBST              =
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
 # of all members will be omitted, etc.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Java. For instance, namespaces will be presented as packages, qualified 
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
 # scopes will look different, etc.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
-# sources only. Doxygen will then generate output that is more tailored for 
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
 # Fortran.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
-# sources. Doxygen will then generate output that is tailored for 
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
 # VHDL.
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
-# Doxygen selects the parser to use depending on the extension of the files it 
-# parses. With this tag you can assign which parser to use for a given 
-# extension. Doxygen has a built-in mapping, but you can override or extend it 
-# using this tag. The format is ext=language, where ext is a file extension, 
-# and language is one of the parsers supported by doxygen: IDL, Java, 
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, 
-# C++. For instance to make doxygen treat .inc files as Fortran files (default 
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note 
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the 
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
 # files are not read by doxygen.
 
-EXTENSION_MAPPING      = 
+EXTENSION_MAPPING      =
 
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all 
-# comments according to the Markdown format, which allows for more readable 
-# documentation. See http://daringfireball.net/projects/markdown/ for details. 
-# The output of markdown processing is further processed by doxygen, so you 
-# can mix doxygen, HTML, and XML commands with Markdown formatting. 
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
 # Disable only in case of backward compatibilities issues.
 
 MARKDOWN_SUPPORT       = YES
 
-# When enabled doxygen tries to link words that correspond to documented classes, 
-# or namespaces to their corresponding documentation. Such a link can be 
-# prevented in individual cases by by putting a % sign in front of the word or 
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
 # globally by setting AUTOLINK_SUPPORT to NO.
 
 AUTOLINK_SUPPORT       = YES
 
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
-# to include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also makes the inheritance and collaboration 
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
 
 BUILTIN_STL_SUPPORT    = NO
 
-# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
-# Doxygen will parse them like normal C++ but will assume all classes use public 
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
 # instead of private inheritance when no explicit protection keyword is present.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate 
-# getter and setter methods for a property. Setting this option to YES (the 
-# default) will make doxygen replace the get and set methods by a property in 
-# the documentation. This will only work if the methods are indeed getting or 
-# setting a simple type. If this is not the case, or you want to show the 
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
 # methods anyway, you should set this option to NO.
 
 IDL_PROPERTY_SUPPORT   = YES
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
-# the same type (for instance a group of public functions) to be put as a 
-# subgroup of that type (e.g. under the Public Functions section). Set it to 
-# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
 # the \nosubgrouping command.
 
 SUBGROUPING            = YES
 
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and 
-# unions are shown inside the group in which they are included (e.g. using 
-# @ingroup) instead of on a separate page (for HTML and Man pages) or 
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
 # section (for LaTeX and RTF).
 
 INLINE_GROUPED_CLASSES = NO
 
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 
-# unions with only public data fields will be shown inline in the documentation 
-# of the scope in which they are defined (i.e. file, namespace, or group 
-# documentation), provided this scope is documented. If set to NO (the default), 
-# structs, classes, and unions are shown on a separate page (for HTML and Man 
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
 # pages) or section (for LaTeX and RTF).
 
 INLINE_SIMPLE_STRUCTS  = NO
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
-# is documented as struct, union, or enum with the name of the typedef. So 
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
-# with name TypeT. When disabled the typedef will appear as a member of a file, 
-# namespace, or class. And the struct will be named TypeS. This can typically 
-# be useful for C code in case the coding convention dictates that all compound 
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
 # types are typedef'ed and only the typedef is referenced, never the tag name.
 
 TYPEDEF_HIDES_STRUCT   = NO
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
-# determine which symbols to keep in memory and which to flush to disk. 
-# When the cache is full, less often used symbols will be written to disk. 
-# For small to medium size projects (<1000 input files) the default value is 
-# probably good enough. For larger projects a too small cache size can cause 
-# doxygen to be busy swapping symbols to and from disk most of the time 
-# causing a significant performance penalty. 
-# If the system has enough physical memory increasing the cache will improve the 
-# performance by keeping more symbols in memory. Note that the value works on 
-# a logarithmic scale so increasing the size by one will roughly double the 
-# memory usage. The cache size is given by this formula: 
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
 # corresponding to a cache size of 2^16 = 65536 symbols.
 
 SYMBOL_CACHE_SIZE      = 0
 
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be 
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given 
-# their name and scope. Since this can be an expensive process and often the 
-# same symbol appear multiple times in the code, doxygen keeps a cache of 
-# pre-resolved symbols. If the cache is too small doxygen will become slower. 
-# If the cache is too large, memory is wasted. The cache size is given by this 
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
 # corresponding to a cache size of 2^16 = 65536 symbols.
 
 LOOKUP_CACHE_SIZE      = 0
@@ -370,329 +370,329 @@
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
 # will be included in the documentation.
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal 
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
 # scope will be included in the documentation.
 
 EXTRACT_PACKAGE        = NO
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
 # will be included in the documentation.
 
 EXTRACT_STATIC         = NO
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
 # If set to NO only classes defined in header files are included.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When set to YES local 
-# methods, which are defined in the implementation section but not in 
-# the interface are included in the documentation. 
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
 # If set to NO (the default) only methods in the interface are included.
 
 EXTRACT_LOCAL_METHODS  = NO
 
-# If this flag is set to YES, the members of anonymous namespaces will be 
-# extracted and appear in the documentation as a namespace called 
-# 'anonymous_namespace{file}', where file will be replaced with the base 
-# name of the file that contains the anonymous namespace. By default 
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
 # anonymous namespaces are hidden.
 
 EXTRACT_ANON_NSPACES   = NO
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
 # This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
 # overviews. This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
 # documentation.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
 # function's detailed documentation block.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
 # Set it to YES to include the internal documentation.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file names in lower-case letters. If set to YES upper-case letters are also 
-# allowed. This is useful if you have classes or files whose names only differ 
-# in case and if your file system supports case sensitive file names. Windows 
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
 
 CASE_SENSE_NAMES       = NO
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
 # documentation. If set to YES the scope will be hidden.
 
 HIDE_SCOPE_NAMES       = YES
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
 # of that file.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 
-# will list include files with double quotes in the documentation 
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
 # rather than with sharp brackets.
 
 FORCE_LOCAL_INCLUDES   = NO
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
 # is inserted in the documentation for inline members.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
 # declaration order.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
 # declaration order.
 
 SORT_BRIEF_DOCS        = NO
 
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen 
-# will sort the (brief and detailed) documentation of class members so that 
-# constructors and destructors are listed first. If set to NO (the default) 
-# the constructors will appear in the respective orders defined by 
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. 
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO 
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
 # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
 
 SORT_MEMBERS_CTORS_1ST = NO
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
-# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
 # the group names will appear in their defined order.
 
 SORT_GROUP_NAMES       = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
-# Note: This option applies only to the class list, not to the 
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
 # alphabetical list.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to 
-# do proper type resolution of all parameters of a function it will reject a 
-# match between the prototype and the implementation of a member function even 
-# if there is only one candidate or it is obvious which candidate to choose 
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen 
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
 # will still accept a match between prototype and implementation in such cases.
 
 STRICT_PROTO_MATCHING  = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
 # commands in the documentation.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
 # commands in the documentation.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
 # commands in the documentation.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
 # \deprecated commands in the documentation.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if section-label ... \endif 
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
 # and \cond section-label ... \endcond blocks.
 
-ENABLED_SECTIONS       = 
+ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or macro consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and macros in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
 MAX_INITIALIZER_LINES  = 26
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
-# at the bottom of the documentation of classes and structs. If set to YES the 
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
 # list will mention the files that were used to generate the documentation.
 
 SHOW_USED_FILES        = YES
 
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
-# This will remove the Files entry from the Quick Index and from the 
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
 # Folder Tree View (if specified). The default is YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index 
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
 # and from the Folder Tree View (if specified). The default is YES.
 
 SHOW_NAMESPACES        = YES
 
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from 
-# the version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
 # is used as the file version. See the manual for examples.
 
-FILE_VERSION_FILTER    = 
+FILE_VERSION_FILTER    =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 
-# by doxygen. The layout file controls the global structure of the generated 
-# output files in an output format independent way. To create the layout file 
-# that represents doxygen's defaults, run doxygen with the -l option. 
-# You can optionally specify a file name after the option, if omitted 
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
 # DoxygenLayout.xml will be used as the name of the layout file.
 
-LAYOUT_FILE            = 
+LAYOUT_FILE            =
 
-# The CITE_BIB_FILES tag can be used to specify one or more bib files 
-# containing the references data. This must be a list of .bib files. The 
-# .bib extension is automatically appended if omitted. Using this command 
-# requires the bibtex tool to be installed. See also 
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 
-# feature you need bibtex and perl available in the search path. Do not use 
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
 # file names with spaces, bibtex cannot handle them.
 
-CITE_BIB_FILES         = 
+CITE_BIB_FILES         =
 
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated 
+# The QUIET tag can be used to turn on/off the messages that are generated
 # by doxygen. Possible values are YES and NO. If left blank NO is used.
 
 QUIET                  = NO
 
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
 # NO is used.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
 # automatically be disabled.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
 # don't exist or using markup commands wrongly.
 
 WARN_IF_DOC_ERROR      = YES
 
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
 # documentation.
 
 WARN_NO_PARAMDOC       = NO
 
-# The WARN_FORMAT tag determines the format of the warning messages that 
-# doxygen can produce. The string should contain the $file, $line, and $text 
-# tags, which will be replaced by the file and line number from which the 
-# warning originated and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
 # be obtained via FILE_VERSION_FILTER)
 
 WARN_FORMAT            = "$file:$line: $text"
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
 # to stderr.
 
-WARN_LOGFILE           = 
+WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
 # configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain 
-# documented source files. You may enter file names like "myfile.cpp" or 
-# directories like "/usr/src/myproject". Separate the files or directories 
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
 INPUT                  = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
 
-# This tag can be used to specify the character encoding of the source files 
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
-# also the default input encoding. Doxygen uses libiconv (or the iconv built 
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
 # the list of possible encodings.
 
 INPUT_ENCODING         = UTF-8
 
-# If the value of the INPUT tag contains directories, you can use the 
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank the following patterns are tested: 
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh 
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
 # *.f90 *.f *.for *.vhd *.vhdl
 
 FILE_PATTERNS          = *.c \
@@ -730,159 +730,159 @@
                          *.vhd \
                          *.vhdl
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
 # If left blank NO is used.
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should be 
-# excluded from the INPUT source files. This way you can easily exclude a 
-# subdirectory from a directory tree whose root is specified with the INPUT tag. 
-# Note that relative paths are relative to the directory from which doxygen is 
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = 
+EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 
-# directories that are symbolic links (a Unix file system feature) are excluded 
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
 
 EXCLUDE_SYMLINKS       = NO
 
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
 # for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = 
+EXCLUDE_PATTERNS       =
 
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the 
-# output. The symbol name can be a fully qualified name, a word, or if the 
-# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
 
-EXCLUDE_SYMBOLS        = 
+EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
 # the \include command).
 
-EXAMPLE_PATH           = 
+EXAMPLE_PATH           =
 
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
 # blank all files are included.
 
 EXAMPLE_PATTERNS       = *
 
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
 # Possible values are YES and NO. If left blank NO is used.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
 # the \image command).
 
-IMAGE_PATH             = 
+IMAGE_PATH             =
 
-# The INPUT_FILTER tag can be used to specify a program that doxygen should 
-# invoke to filter for each input file. Doxygen will invoke the filter program 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be
 # ignored.
 
-INPUT_FILTER           = 
+INPUT_FILTER           =
 
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis.  Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match.  The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty or if 
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.  Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.  The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
 # non of the patterns match the file name, INPUT_FILTER is applied.
 
-FILTER_PATTERNS        = 
+FILTER_PATTERNS        =
 
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
 
 FILTER_SOURCE_FILES    = NO
 
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file 
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) 
-# and it is also possible to disable source filtering for a specific pattern 
-# using *.ext= (so without naming a filter). This option only has effect when 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
 # FILTER_SOURCE_FILES is enabled.
 
-FILTER_SOURCE_PATTERNS = 
+FILTER_SOURCE_PATTERNS =
 
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that 
-# is part of the input, its contents will be placed on the main page (index.html). 
-# This can be useful if you have a project on for instance GitHub and want reuse 
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
 # the introduction page also for the doxygen output.
 
-USE_MDFILE_AS_MAINPAGE = 
+USE_MDFILE_AS_MAINPAGE =
 
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
 # VERBATIM_HEADERS is set to NO.
 
 SOURCE_BROWSER         = NO
 
-# Setting the INLINE_SOURCES tag to YES will include the body 
+# Setting the INLINE_SOURCES tag to YES will include the body
 # of functions and classes directly in the documentation.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
 # fragments. Normal C, C++ and Fortran comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = NO
 
-# If the REFERENCED_BY_RELATION tag is set to YES 
-# then for each documented function all documented 
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
 # functions referencing it will be listed.
 
 REFERENCED_BY_RELATION = NO
 
-# If the REFERENCES_RELATION tag is set to YES 
-# then for each documented function all documented entities 
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
 # called/used by that function will be listed.
 
 REFERENCES_RELATION    = NO
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
 # link to the source code.  Otherwise they will link to the documentation.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code 
-# will point to the HTML generated by the htags(1) tool instead of doxygen 
-# built-in source browser. The htags tool is part of GNU's global source 
-# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
 # will need version 4.8.6 or higher.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
 # which an include is specified. Set to NO to disable this.
 
 VERBATIM_HEADERS       = NO
@@ -891,170 +891,170 @@
 # configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
 # contains a lot of classes, structs, unions or interfaces.
 
 ALPHABETICAL_INDEX     = NO
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
 # in which this list will be split (can be a number in the range [1..20])
 
 COLS_IN_ALPHA_INDEX    = 5
 
-# In case all classes in a project start with a common prefix, all 
-# classes will be put under the same header in the alphabetical index. 
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
 # should be ignored while generating the index headers.
 
-IGNORE_PREFIX          = 
+IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
 # configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
 # generate HTML output.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `html' will be used as the default path.
 
 HTML_OUTPUT            = $(HTML_OUTPUT)
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
 # doxygen will generate files with .html extension.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard header. Note that when using a custom header you are responsible  
-# for the proper inclusion of any scripts and style sheets that doxygen 
-# needs, which is dependent on the configuration options used. 
-# It is advised to generate a default header using "doxygen -w html 
-# header.html footer.html stylesheet.css YourConfigFile" and then modify 
-# that header. Note that the header is subject to change so you typically 
-# have to redo this when upgrading to a newer version of doxygen or when 
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
 # changing the value of configuration settings such as GENERATE_TREEVIEW!
 
 HTML_HEADER            = $(HTML_HEADER)
 
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
 # standard footer.
 
 HTML_FOOTER            = $(HTML_FOOTER)
 
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If left blank doxygen will 
-# generate a default style sheet. Note that it is recommended to use 
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this 
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
 # tag will in the future become obsolete.
 
-HTML_STYLESHEET        = 
+HTML_STYLESHEET        =
 
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional 
-# user-defined cascading style sheet that is included after the standard 
-# style sheets created by doxygen. Using this option one can overrule 
-# certain style aspects. This is preferred over using HTML_STYLESHEET 
-# since it does not replace the standard style sheet and is therefor more 
-# robust against future updates. Doxygen will copy the style sheet file to 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
 # the output directory.
 
-HTML_EXTRA_STYLESHEET  = 
+HTML_EXTRA_STYLESHEET  =
 
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 
-# other source files which should be copied to the HTML output directory. Note 
-# that these files will be copied to the base HTML output directory. Use the 
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these 
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that 
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
 # the files will be copied as-is; there are no commands or markers available.
 
-HTML_EXTRA_FILES       = 
+HTML_EXTRA_FILES       =
 
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 
-# Doxygen will adjust the colors in the style sheet and background images 
-# according to this color. Hue is specified as an angle on a colorwheel, 
-# see http://en.wikipedia.org/wiki/Hue for more information. 
-# For instance the value 0 represents red, 60 is yellow, 120 is green, 
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
 # The allowed range is 0 to 359.
 
 HTML_COLORSTYLE_HUE    = 220
 
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 
-# the colors in the HTML output. For a value of 0 the output will use 
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
 # grayscales only. A value of 255 will produce the most vivid colors.
 
 HTML_COLORSTYLE_SAT    = 0
 
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 
-# the luminance component of the colors in the HTML output. Values below 
-# 100 gradually make the output lighter, whereas values above 100 make 
-# the output darker. The value divided by 100 is the actual gamma applied, 
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
 # and 100 does not change the gamma.
 
 HTML_COLORSTYLE_GAMMA  = 80
 
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
-# page will contain the date and time when the page was generated. Setting 
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
 # this to NO can help when comparing the output of multiple runs.
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
-# documentation will contain sections that can be hidden and shown after the 
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
 # page has loaded.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of 
-# entries shown in the various tree structured indices initially; the user 
-# can expand and collapse entries dynamically later on. Doxygen will expand 
-# the tree to such a level that at most the specified number of entries are 
-# visible (unless a fully collapsed tree already exceeds this amount). 
-# So setting the number of entries 1 will produce a full collapsed tree by 
-# default. 0 is a special value representing an infinite number of entries 
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
 # and will result in a full expanded tree by default.
 
 HTML_INDEX_NUM_ENTRIES = 100
 
-# If the GENERATE_DOCSET tag is set to YES, additional index files 
-# will be generated that can be used as input for Apple's Xcode 3 
-# integrated development environment, introduced with OSX 10.5 (Leopard). 
-# To create a documentation set, doxygen will generate a Makefile in the 
-# HTML output directory. Running make will produce the docset in that 
-# directory and running "make install" will install the docset in 
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
-# it at startup. 
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html 
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
 # for more information.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
-# feed. A documentation feed provides an umbrella under which multiple 
-# documentation sets from a single provider (such as a company or product suite) 
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
 # can be grouped.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
-# should uniquely identify the documentation set bundle. This should be a 
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
 # will append .docset to the name.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely 
-# identify the documentation publisher. This should be a reverse domain-name 
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
 # style string, e.g. com.mycompany.MyDocSet.documentation.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
@@ -1063,361 +1063,361 @@
 
 DOCSET_PUBLISHER_NAME  = Publisher
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
 # of the generated HTML documentation.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
 # written to the html output directory.
 
-CHM_FILE               = 
+CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
 # the HTML help compiler on the generated index.hhp.
 
-HHC_LOCATION           = 
+HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
 # it should be included in the master .chm file (NO).
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
 # content.
 
-CHM_INDEX_ENCODING     = 
+CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
 # normal table of contents (NO) in the .chm file.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
 # to the contents of the HTML help documentation and to the tree view.
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and 
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated 
-# that can be used as input for Qt's qhelpgenerator to generate a 
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
 # Qt Compressed Help (.qch) of the generated HTML documentation.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
-# be used to specify the file name of the resulting .qch file. 
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
 # The path specified is relative to the HTML output folder.
 
-QCH_FILE               = 
+QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#namespace
 
 QHP_NAMESPACE          = org.doxygen.Project
 
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to 
-# add. For more information please see 
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#custom-filters
 
-QHP_CUST_FILTER_NAME   = 
+QHP_CUST_FILTER_NAME   =
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the 
-# custom filter to add. For more information please see 
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> 
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
 # Qt Help Project / Custom Filters</a>.
 
-QHP_CUST_FILTER_ATTRS  = 
+QHP_CUST_FILTER_ATTRS  =
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this 
-# project's 
-# filter section matches. 
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> 
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
 # Qt Help Project / Filter Attributes</a>.
 
-QHP_SECT_FILTER_ATTRS  = 
+QHP_SECT_FILTER_ATTRS  =
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
-# be used to specify the location of Qt's qhelpgenerator. 
-# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
 # .qhp file.
 
-QHG_LOCATION           = 
+QHG_LOCATION           =
 
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files  
-# will be generated, which together with the HTML files, form an Eclipse help 
-# plugin. To install this plugin and make it available under the help contents 
-# menu in Eclipse, the contents of the directory containing the HTML and XML 
-# files needs to be copied into the plugins directory of eclipse. The name of 
-# the directory within the plugins directory should be the same as 
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before 
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
 # the help appears.
 
 GENERATE_ECLIPSEHELP   = NO
 
-# A unique identifier for the eclipse help plugin. When installing the plugin 
-# the directory name containing the HTML and XML files should also have 
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
 # this name.
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 
-# at top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it. Since the tabs have the same information as the 
-# navigation tree you can set this option to NO if you already set 
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
 # GENERATE_TREEVIEW to YES.
 
 DISABLE_INDEX          = YES
 
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
-# structure should be generated to display hierarchical information. 
-# If the tag value is set to YES, a side panel will be generated 
-# containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 
-# Windows users are probably better off using the HTML help feature. 
-# Since the tree basically has the same information as the tab index you 
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
 # could consider to set DISABLE_INDEX to NO when enabling this option.
 
 GENERATE_TREEVIEW      = NO
 
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML 
-# documentation. Note that a value of 0 will completely suppress the enum 
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
 # values from appearing in the overview section.
 
 ENUM_VALUES_PER_LINE   = 4
 
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
 # is shown.
 
 TREEVIEW_WIDTH         = 250
 
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open 
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
 # links to external symbols imported via tag files in a separate window.
 
 EXT_LINKS_IN_WINDOW    = NO
 
-# Use this tag to change the font size of Latex formulas included 
-# as images in the HTML documentation. The default is 10. Note that 
-# when you change the font size after a successful doxygen run you need 
-# to manually remove any form_*.png images from the HTML output directory 
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
 # to force them to be regenerated.
 
 FORMULA_FONTSIZE       = 10
 
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images 
-# generated for formulas are transparent PNGs. Transparent PNGs are 
-# not supported properly for IE 6.0, but are supported on all modern browsers. 
-# Note that when changing this option you need to delete any form_*.png files 
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
 # in the HTML output before the changes have effect.
 
 FORMULA_TRANSPARENT    = YES
 
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax 
-# (see http://www.mathjax.org) which uses client side Javascript for the 
-# rendering instead of using prerendered bitmaps. Use this if you do not 
-# have LaTeX installed or if you want to formulas look prettier in the HTML 
-# output. When enabled you may also need to install MathJax separately and 
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
 # configure the path to it using the MATHJAX_RELPATH option.
 
 USE_MATHJAX            = NO
 
-# When MathJax is enabled you can set the default output format to be used for 
-# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and 
-# SVG. The default value is HTML-CSS, which is slower, but has the best 
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
 # compatibility.
 
 MATHJAX_FORMAT         = HTML-CSS
 
-# When MathJax is enabled you need to specify the location relative to the 
-# HTML output directory using the MATHJAX_RELPATH option. The destination 
-# directory should contain the MathJax.js script. For instance, if the mathjax 
-# directory is located at the same level as the HTML output directory, then 
-# MATHJAX_RELPATH should be ../mathjax. The default value points to 
-# the MathJax Content Delivery Network so you can quickly see the result without 
-# installing MathJax.  However, it is strongly recommended to install a local 
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.  However, it is strongly recommended to install a local
 # copy of MathJax from http://www.mathjax.org before deployment.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
 
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension 
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
 # names that should be enabled during MathJax rendering.
 
-MATHJAX_EXTENSIONS     = 
+MATHJAX_EXTENSIONS     =
 
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box 
-# for the HTML output. The underlying search engine uses javascript 
-# and DHTML and should work on any modern browser. Note that when using 
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets 
-# (GENERATE_DOCSET) there is already a search function so this one should 
-# typically be disabled. For large projects the javascript based search engine 
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
 # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
 
 SEARCHENGINE           = NO
 
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
-# implemented using a web server instead of a web client using Javascript. 
-# There are two flavours of web server based search depending on the 
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for 
-# searching and an index file used by the script. When EXTERNAL_SEARCH is 
-# enabled the indexing and searching needs to be provided by external tools. 
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
 # See the manual for details.
 
 SERVER_BASED_SEARCH    = NO
 
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP 
-# script for searching. Instead the search results are written to an XML file 
-# which needs to be processed by an external indexer. Doxygen will invoke an 
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain 
-# the search results. Doxygen ships with an example indexer (doxyindexer) and 
-# search engine (doxysearch.cgi) which are based on the open source search engine 
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
 # library Xapian. See the manual for configuration details.
 
 EXTERNAL_SEARCH        = NO
 
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server 
-# which will returned the search results when EXTERNAL_SEARCH is enabled. 
-# Doxygen ships with an example search engine (doxysearch) which is based on 
-# the open source search engine library Xapian. See the manual for configuration 
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
 # details.
 
-SEARCHENGINE_URL       = 
+SEARCHENGINE_URL       =
 
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed 
-# search data is written to a file for indexing by an external tool. With the 
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
 # SEARCHDATA_FILE tag the name of this file can be specified.
 
 SEARCHDATA_FILE        = searchdata.xml
 
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the 
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is 
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple 
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
 # projects and redirect the results back to the right project.
 
-EXTERNAL_SEARCH_ID     = 
+EXTERNAL_SEARCH_ID     =
 
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen 
-# projects other than the one defined by this configuration file, but that are 
-# all added to the same external search index. Each project needs to have a 
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id 
-# of to a relative location where the documentation can be found. 
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
 # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
 
-EXTRA_SEARCH_MAPPINGS  = 
+EXTRA_SEARCH_MAPPINGS  =
 
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
 # generate Latex output.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `latex' will be used as the default path.
 
 LATEX_OUTPUT           = latex
 
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name. 
-# Note that when enabling USE_PDFLATEX this option is only used for 
-# generating bitmaps for formulas in the HTML output, but not in the 
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
 # Makefile that is written to the output directory.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
-# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
 # default command name.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
-# LaTeX documents. This may be useful for small projects and may help to 
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, letter, legal and 
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
 # executive. If left blank a4wide will be used.
 
 PAPER_TYPE             = a4
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
 # packages that should be included in the LaTeX output.
 
-EXTRA_PACKAGES         = 
+EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
 # standard header. Notice: only use this tag if you know what you are doing!
 
-LATEX_HEADER           = 
+LATEX_HEADER           =
 
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for 
-# the generated latex document. The footer should contain everything after 
-# the last chapter. If it is left blank doxygen will generate a 
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
 # standard footer. Notice: only use this tag if you know what you are doing!
 
-LATEX_FOOTER           = 
+LATEX_FOOTER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
 # This makes the output suitable for online browsing using a pdf viewer.
 
 PDF_HYPERLINKS         = YES
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
 # higher quality PDF documentation.
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
 # This option is also used when generating formulas in HTML.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
 # in the output.
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include 
-# source code with syntax highlighting in the LaTeX output. 
-# Note that which sources are shown also depends on other settings 
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
 # such as SOURCE_BROWSER.
 
 LATEX_SOURCE_CODE      = NO
 
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the 
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See 
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
 # http://en.wikipedia.org/wiki/BibTeX for more info.
 
 LATEX_BIB_STYLE        = plain
@@ -1426,68 +1426,68 @@
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
 # other RTF readers or editors.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `rtf' will be used as the default path.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
 # Note: wordpad (write) and others do not support links.
 
 RTF_HYPERLINKS         = NO
 
-# Load style sheet definitions from file. Syntax is similar to doxygen's 
-# config file, i.e. a series of assignments. You only have to provide 
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
 # replacements, missing definitions are set to their default value.
 
-RTF_STYLESHEET_FILE    = 
+RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document. 
+# Set optional variables used in the generation of an rtf document.
 # Syntax is similar to doxygen's config file.
 
-RTF_EXTENSIONS_FILE    = 
+RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
 # configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
 # generate man pages
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `man' will be used as the default path.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to 
+# The MAN_EXTENSION tag determines the extension that is added to
 # the generated man pages (default is the subroutine's section .3)
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
 # would be unable to find the correct page. The default is NO.
 
 MAN_LINKS              = NO
@@ -1496,33 +1496,33 @@
 # configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
 # the code including all documentation.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `xml' will be used as the default path.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_SCHEMA             = 
+XML_SCHEMA             =
 
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_DTD                = 
+XML_DTD                =
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
 # enabling this will significantly increase the size of the XML output.
 
 XML_PROGRAMLISTING     = YES
@@ -1531,10 +1531,10 @@
 # configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see autogen.sf.net) file 
-# that captures the structure of the code including all 
-# documentation. Note that this feature is still experimental 
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
 # and incomplete at the moment.
 
 GENERATE_AUTOGEN_DEF   = NO
@@ -1543,97 +1543,97 @@
 # configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
 # moment.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
 # to generate PDF and DVI output from the Perl module output.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader.  This is useful 
-# if you want to understand what is going on.  On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
 # and Perl will parse it just the same.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
 # Makefile don't overwrite each other's variables.
 
-PERLMOD_MAKEVAR_PREFIX = 
+PERLMOD_MAKEVAR_PREFIX =
 
 #---------------------------------------------------------------------------
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
 # files.
 
-ENABLE_PREPROCESSING   = YES
+ENABLE_PREPROCESSING   = NO
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
 # way by setting EXPAND_ONLY_PREDEF to YES.
 
 MACRO_EXPANSION        = NO
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
 # PREDEFINED and EXPAND_AS_DEFINED tags.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
 # pointed to by INCLUDE_PATH will be searched when a #include is found.
 
 SEARCH_INCLUDES        = YES
 
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
 # the preprocessor.
 
-INCLUDE_PATH           = 
+INCLUDE_PATH           =
 
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
-# patterns (like *.h and *.hpp) to filter out the header-files in the 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
 # be used.
 
-INCLUDE_FILE_PATTERNS  = 
+INCLUDE_FILE_PATTERNS  =
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = 
+PREDEFINED             =
 
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
-# Use the PREDEFINED tag if you want to use a different macro definition that 
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
 # overrules the definition found in the source code.
 
-EXPAND_AS_DEFINED      = 
+EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all references to function-like macros 
-# that are alone on a line, have an all uppercase name, and do not end with a 
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
 # semicolon, because these will confuse the parser if not removed.
 
 SKIP_FUNCTION_MACROS   = YES
@@ -1642,37 +1642,37 @@
 # Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles. For each 
-# tag file the location of the external documentation should be added. The 
-# format of a tag file without this location is as follows: 
-#   TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths 
-# or URLs. Note that each tag file must have a unique name (where the name does 
-# NOT include the path). If a tag file is not located in the directory in which 
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
 # doxygen is run, you must also specify the path to the tagfile here.
 
-TAGFILES               = 
+TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
 # a tag file that is based on the input files it reads.
 
-GENERATE_TAGFILE       = 
+GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
 # will be listed.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
 # be listed.
 
 EXTERNAL_GROUPS        = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script 
+# The PERL_PATH should be the absolute path and name of the perl script
 # interpreter (i.e. the result of `which perl').
 
 PERL_PATH              = /usr/bin/perl
@@ -1681,222 +1681,222 @@
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option also works with HAVE_DOT disabled, but it is recommended to 
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
 # install and use dot, since it yields more powerful graphs.
 
 CLASS_DIAGRAMS         = NO
 
-# You can define message sequence charts within doxygen comments using the \msc 
-# command. Doxygen will then run the mscgen tool (see 
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
-# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
 # default search path.
 
-MSCGEN_PATH            = 
+MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
 # or is not a class.
 
 HIDE_UNDOC_RELATIONS   = YES
 
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
 # have no effect if this option is set to NO (the default)
 
 HAVE_DOT               = NO
 
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is 
-# allowed to run in parallel. When set to 0 (the default) doxygen will 
-# base this on the number of processors available in the system. You can set it 
-# explicitly to a value larger than 0 to get control over the balance 
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
 # between CPU load and processing speed.
 
 DOT_NUM_THREADS        = 0
 
-# By default doxygen will use the Helvetica font for all dot files that 
-# doxygen generates. When you want a differently looking font you can specify 
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find 
-# the font, which can be done by putting it in a standard location or by setting 
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the 
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
 # directory containing the font.
 
 DOT_FONTNAME           = Helvetica
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
 # The default size is 10pt.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the Helvetica font. 
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to 
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
 # set the path where dot can find it.
 
-DOT_FONTPATH           = 
+DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
 # CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
 # class references variables) of the class with other documented classes.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
 # will generate a graph for groups, showing the direct groups dependencies
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
-# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
 
 UML_LOOK               = NO
 
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside 
-# the class node. If there are many fields or methods and many nodes the 
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS 
-# threshold limits the number of items for each type to make the size more 
-# managable. Set this to 0 for no limit. Note that the threshold may be 
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
 # exceeded by 50% before the limit is enforced.
 
 UML_LIMIT_NUM_FIELDS   = 10
 
-# If set to YES, the inheritance and collaboration graphs will show the 
+# If set to YES, the inheritance and collaboration graphs will show the
 # relations between templates and their instances.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
 # other documented files.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
 # indirectly include this file.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
-# doxygen will generate a call dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable call graphs 
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
 # for selected functions only using the \callgraph command.
 
 CALL_GRAPH             = NO
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
-# doxygen will generate a caller dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable caller 
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
 # graphs for selected functions only using the \callergraph command.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
 # will generate a graphical hierarchy of all classes instead of a textual one.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
-# in a graphical way. The dependency relations are determined by the #include 
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
 
 DIRECTORY_GRAPH        = YES
 
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
-# generated by dot. Possible values are svg, png, jpg, or gif. 
-# If left blank png will be used. If you choose svg you need to set 
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
 # visible in IE 9+ (other browsers do not have this requirement).
 
 DOT_IMAGE_FORMAT       = png
 
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to 
-# enable generation of interactive SVG images that allow zooming and panning. 
-# Note that this requires a modern browser other than Internet Explorer. 
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you 
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
 # visible. Older versions of IE do not have SVG support.
 
 INTERACTIVE_SVG        = NO
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
 
-DOT_PATH               = 
+DOT_PATH               =
 
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
 # \dotfile command).
 
-DOTFILE_DIRS           = 
+DOTFILE_DIRS           =
 
-# The MSCFILE_DIRS tag can be used to specify one or more directories that 
-# contain msc files that are included in the documentation (see the 
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
 # \mscfile command).
 
-MSCFILE_DIRS           = 
+MSCFILE_DIRS           =
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the 
-# number of direct children of the root node in a graph is already larger than 
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
 
 DOT_GRAPH_MAX_NODES    = 50
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, because dot on Windows does not 
-# seem to support this out of the box. Warning: Depending on the platform used, 
-# enabling this option may lead to badly anti-aliased labels on the edges of 
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
 # a graph (i.e. they become hard to read).
 
 DOT_TRANSPARENT        = NO
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
 # support this, this feature is disabled by default.
 
 DOT_MULTI_TARGETS      = NO
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
 # arrows in the dot generated graphs.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
 # the various graphs.
 
 DOT_CLEANUP            = YES
diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h
index d654839..7ef3ecb 100644
--- a/include/android/asset_manager.h
+++ b/include/android/asset_manager.h
@@ -26,6 +26,9 @@
 #ifndef ANDROID_ASSET_MANAGER_H
 #define ANDROID_ASSET_MANAGER_H
 
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -131,6 +134,7 @@
  */
 off_t AAsset_seek(AAsset* asset, off_t offset, int whence);
 
+#if __ANDROID_API__ >= 13
 /**
  * Seek to the specified offset within the asset data.  'whence' uses the
  * same constants as lseek()/fseek().
@@ -141,6 +145,7 @@
  * 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.
@@ -159,23 +164,27 @@
  */
 off_t AAsset_getLength(AAsset* asset);
 
+#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);
 
+#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
@@ -187,6 +196,7 @@
  */
 int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength);
 
+#if __ANDROID_API__ >= 13
 /**
  * Open a new file descriptor that can be used to read the asset data.
  *
@@ -197,6 +207,7 @@
  * 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/bitmap.h b/include/android/bitmap.h
index 261e64f..2def64d 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -56,9 +56,9 @@
     ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
     /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
     ANDROID_BITMAP_FORMAT_RGB_565   = 4,
-    /** Red: 4 bits, Green: 4 bits, Blue: 4 bits, Alpha: 4 bits. **/
+    /** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/
     ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
-    /** Deprecated. */
+    /** Alpha: 8 bits. */
     ANDROID_BITMAP_FORMAT_A_8       = 8,
 };
 
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 02c83dc..43346fe 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -30,6 +30,8 @@
 
 __BEGIN_DECLS
 
+#if __ANDROID_API__ >= 24
+
 struct AChoreographer;
 typedef struct AChoreographer AChoreographer;
 
@@ -62,6 +64,9 @@
  */
 void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
                 AChoreographer_frameCallback callback, void* data, long delayMillis);
+
+#endif /* __ANDROID_API__ >= 24 */
+
 __END_DECLS
 
 #endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 81f71a9..6287332 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -26,6 +26,8 @@
 #ifndef ANDROID_CONFIGURATION_H
 #define ANDROID_CONFIGURATION_H
 
+#include <sys/cdefs.h>
+
 #include <android/asset_manager.h>
 
 #ifdef __cplusplus
@@ -265,6 +267,36 @@
     ACONFIGURATION_SCREENROUND_NO = 0x1,
     ACONFIGURATION_SCREENROUND_YES = 0x2,
 
+    /** Wide color gamut: not specified. */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
+    /**
+     * Wide color gamut: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+     * nowidecg</a> resource qualifier specified.
+     */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
+    /**
+     * Wide color gamut: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+     * widecg</a> resource qualifier specified.
+     */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
+
+    /** HDR: not specified. */
+    ACONFIGURATION_HDR_ANY = 0x00,
+    /**
+     * HDR: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * lowdr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_HDR_NO = 0x1,
+    /**
+     * HDR: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * highdr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_HDR_YES = 0x2,
+
     /** UI mode: not specified. */
     ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
     /**
@@ -298,6 +330,11 @@
      * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
+    /**
+     * UI mode: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
 
     /** UI night mode: not specified.*/
     ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
@@ -424,6 +461,12 @@
     ACONFIGURATION_LAYOUTDIR = 0x4000,
     ACONFIGURATION_SCREEN_ROUND = 0x8000,
     /**
+     * Bit mask for
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+     * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+     */
+    ACONFIGURATION_COLOR_MODE = 0x10000,
+    /**
      * Constant used to to represent MNC (Mobile Network Code) zero.
      * 0 cannot be used, since it is used to represent an undefined MNC.
      */
@@ -628,6 +671,7 @@
  */
 void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
 
+#if __ANDROID_API__ >= 13
 /**
  * Return the current configuration screen width in dp units, or
  * ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set.
@@ -660,7 +704,9 @@
  * Set the configuration's smallest screen width in dp units.
  */
 void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value);
+#endif /* __ANDROID_API__ >= 13 */
 
+#if __ANDROID_API__ >= 17
 /**
  * Return the configuration's layout direction, or
  * ACONFIGURATION_LAYOUTDIR_ANY if not set.
@@ -671,6 +717,7 @@
  * Set the configuration's layout direction.
  */
 void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value);
+#endif /* __ANDROID_API__ >= 17 */
 
 /**
  * Perform a diff between two configurations.  Returns a bit mask of
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
new file mode 100644
index 0000000..6020870
--- /dev/null
+++ b/include/android/hardware_buffer_jni.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file hardware_buffer_jni.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_JNI_H
+#define ANDROID_HARDWARE_BUFFER_JNI_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * Return the AHardwareBuffer associated with a Java HardwareBuffer object,
+ * for interacting with it through native code.  This acquires a reference
+ * on the AHardwareBuffer that is returned; be sure to use
+ * AHardwareBuffer_release() when done with it so that it doesn't leak.
+ */
+AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
+        jobject hardwareBufferObj);
+
+/**
+ * Return a new Java HardwareBuffer object that wraps the passed native
+ * AHardwareBuffer object.
+ */
+jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
+        AHardwareBuffer* hardwareBuffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_JNI_H
diff --git a/include/android/input.h b/include/android/input.h
index fd9fa98..0829989 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -26,6 +26,8 @@
 #ifndef _ANDROID_INPUT_H
 #define _ANDROID_INPUT_H
 
+#include <sys/cdefs.h>
+
 /******************************************************************
  *
  * IMPORTANT NOTICE:
@@ -833,6 +835,8 @@
     AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS,
     /** trackball */
     AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
+    /** mouse relative */
+    AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION,
     /** touchpad */
     AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
     /** navigation */
@@ -978,8 +982,10 @@
  */
 int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
 
+#if __ANDROID_API__ >= 14
 /** Get the button state of all buttons that are pressed. */
 int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
+#endif
 
 /**
  * Get a bitfield indicating which edges, if any, were touched by this motion event.
@@ -1044,12 +1050,14 @@
  */
 int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
 
+#if __ANDROID_API__ >= 14
 /**
  * Get the tool type of a pointer for the given pointer index.
  * The tool type indicates the type of tool used to make contact such as a
  * finger or stylus, if known.
  */
 int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index);
+#endif
 
 /**
  * Get the original raw X coordinate of this event.
@@ -1139,9 +1147,11 @@
  */
 float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
 
+#if __ANDROID_API__ >= 13
 /** Get the value of the request axis for the given pointer index. */
 float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
         int32_t axis, size_t pointer_index);
+#endif
 
 /**
  * Get the number of historical points in this event.  These are movements that
@@ -1272,12 +1282,14 @@
 float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
+#if __ANDROID_API__ >= 13
 /**
  * Get the historical value of the request axis for the given pointer index
  * that occurred between this event and the previous motion event.
  */
 float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
         int32_t axis, size_t pointer_index, size_t history_index);
+#endif
 
 
 struct AInputQueue;
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 6c718c9..97892f8 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -51,16 +51,15 @@
  * on failure with an appropriate errno value set.
  */
 
+#if __ANDROID_API__ >= 24
 
 /**
  * Set the network to be used by the given socket file descriptor.
  *
- * To clear a previous socket binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous socket binding, invoke with NETWORK_UNSPECIFIED.
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket))
  *
- *     [ android.net.Network#bindSocket() ]
- *     https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)
  */
 int android_setsocknetwork(net_handle_t network, int fd);
 
@@ -74,12 +73,10 @@
  * resolutions will fail.  This is by design so an application doesn't
  * accidentally use sockets it thinks are still bound to a particular network.
  *
- * To clear a previous process binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
  *
- *     [ android.net.ConnectivityManager#setProcessDefaultNetwork() ]
- *     https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)
  */
 int android_setprocnetwork(net_handle_t network);
 
@@ -95,15 +92,15 @@
  *     - either |node| or |service| may be NULL, but not both
  *     - |res| must not be NULL
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String))
  *
- *     [ android.net.Network#getAllByName() ]
- *     https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)
  */
 int android_getaddrinfofornetwork(net_handle_t network,
         const char *node, const char *service,
         const struct addrinfo *hints, struct addrinfo **res);
 
+#endif /* __ANDROID_API__ >= 24 */
+
 __END_DECLS
 
 #endif  // ANDROID_MULTINETWORK_H
diff --git a/include/android/native_window.h b/include/android/native_window.h
deleted file mode 100644
index cf07f1a..0000000
--- a/include/android/native_window.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @addtogroup NativeActivity Native Activity
- * @{
- */
-
-/**
- * @file native_window.h
- */
-
-#ifndef ANDROID_NATIVE_WINDOW_H
-#define ANDROID_NATIVE_WINDOW_H
-
-#include <android/rect.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Pixel formats that a window can use.
- */
-enum {
-    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
-    WINDOW_FORMAT_RGBA_8888          = 1,
-    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
-    WINDOW_FORMAT_RGBX_8888          = 2,
-    /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
-    WINDOW_FORMAT_RGB_565            = 4,
-};
-
-struct ANativeWindow;
-/**
- * {@link ANativeWindow} is opaque type that provides access to a native window.
- *
- * A pointer can be obtained using ANativeWindow_fromSurface().
- */
-typedef struct ANativeWindow ANativeWindow;
-
-/**
- * {@link ANativeWindow} is a struct that represents a windows buffer.
- *
- * A pointer can be obtained using ANativeWindow_lock().
- */
-typedef struct ANativeWindow_Buffer {
-    // The number of pixels that are show horizontally.
-    int32_t width;
-
-    // The number of pixels that are shown vertically.
-    int32_t height;
-
-    // The number of *pixels* that a line in the buffer takes in
-    // memory.  This may be >= width.
-    int32_t stride;
-
-    // The format of the buffer.  One of WINDOW_FORMAT_*
-    int32_t format;
-
-    // The actual bits.
-    void* bits;
-
-    // Do not touch.
-    uint32_t reserved[6];
-} ANativeWindow_Buffer;
-
-/**
- * Acquire a reference on the given ANativeWindow object.  This prevents the object
- * from being deleted until the reference is removed.
- */
-void ANativeWindow_acquire(ANativeWindow* window);
-
-/**
- * Remove a reference that was previously acquired with ANativeWindow_acquire().
- */
-void ANativeWindow_release(ANativeWindow* window);
-
-/**
- * Return the current width in pixels of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getWidth(ANativeWindow* window);
-
-/**
- * Return the current height in pixels of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getHeight(ANativeWindow* window);
-
-/**
- * Return the current pixel format of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getFormat(ANativeWindow* window);
-
-/**
- * Change the format and size of the window buffers.
- *
- * The width and height control the number of pixels in the buffers, not the
- * dimensions of the window on screen.  If these are different than the
- * window's physical size, then it buffer will be scaled to match that size
- * when compositing it to the screen.
- *
- * For all of these parameters, if 0 is supplied then the window's base
- * value will come back in force.
- *
- * width and height must be either both zero or both non-zero.
- *
- */
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
-        int32_t width, int32_t height, int32_t format);
-
-/**
- * Lock the window's next drawing surface for writing.
- * inOutDirtyBounds is used as an in/out parameter, upon entering the
- * function, it contains the dirty region, that is, the region the caller
- * intends to redraw. When the function returns, inOutDirtyBounds is updated
- * with the actual area the caller needs to redraw -- this region is often
- * extended by ANativeWindow_lock.
- */
-int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
-        ARect* inOutDirtyBounds);
-
-/**
- * Unlock the window's drawing surface after previously locking it,
- * posting the new buffer to the display.
- */
-int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
-
-#ifdef __cplusplus
-};
-#endif
-
-#endif // ANDROID_NATIVE_WINDOW_H
-
-/** @} */
diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h
index 60a36c3..23b39aa 100644
--- a/include/android/native_window_jni.h
+++ b/include/android/native_window_jni.h
@@ -26,6 +26,8 @@
 #ifndef ANDROID_NATIVE_WINDOW_JNI_H
 #define ANDROID_NATIVE_WINDOW_JNI_H
 
+#include <sys/cdefs.h>
+
 #include <android/native_window.h>
 
 #include <jni.h>
@@ -42,6 +44,27 @@
  */
 ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
 
+#if __ANDROID_API__ >= 13
+/**
+ * Return the ANativeWindow associated with a Java SurfaceTexture object,
+ * for interacting with it through native code.  This acquires a reference
+ * on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
+ * when done with it so that it doesn't leak.
+ */
+ANativeWindow* ANativeWindow_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture);
+#endif
+
+#if __ANDROID_API__ >= 26
+/**
+ * Return a Java Surface object derived from the ANativeWindow, for interacting
+ * with it through Java code. The returned Java object acquires a reference on
+ * the ANativeWindow; maintains it through general Java object's life cycle;
+ * and will automatically release the reference when the Java object gets garbage
+ * collected.
+ */
+jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window);
+#endif
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 5a61213..cdb3fff 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -48,14 +48,21 @@
  *
  */
 
-#include <sys/types.h>
-
 #include <android/looper.h>
 
+#include <sys/types.h>
+#include <math.h>
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+#define ASENSOR_RESOLUTION_INVALID     (nanf(""))
+#define ASENSOR_FIFO_COUNT_INVALID     (-1)
+#define ASENSOR_DELAY_INVALID          INT32_MIN
 
 /**
  * Sensor types.
@@ -63,6 +70,10 @@
  */
 enum {
     /**
+     * Invalid sensor type. Returned by {@link ASensor_getType} as error value.
+     */
+    ASENSOR_TYPE_INVALID = -1,
+    /**
      * {@link ASENSOR_TYPE_ACCELEROMETER}
      * reporting-mode: continuous
      *
@@ -134,6 +145,8 @@
  * Sensor Reporting Modes.
  */
 enum {
+    /** invalid reporting mode */
+    AREPORTING_MODE_INVALID = -1,
     /** continuous reporting */
     AREPORTING_MODE_CONTINUOUS = 0,
     /** reporting on change */
@@ -144,6 +157,30 @@
     AREPORTING_MODE_SPECIAL_TRIGGER = 3
 };
 
+/**
+ * Sensor Direct Report Rates.
+ */
+enum {
+    /** stopped */
+    ASENSOR_DIRECT_RATE_STOP = 0,
+    /** nominal 50Hz */
+    ASENSOR_DIRECT_RATE_NORMAL = 1,
+    /** nominal 200Hz */
+    ASENSOR_DIRECT_RATE_FAST = 2,
+    /** nominal 800Hz */
+    ASENSOR_DIRECT_RATE_VERY_FAST = 3
+};
+
+/**
+ * Sensor Direct Channel Type.
+ */
+enum {
+    /** shared memory created by ASharedMemory_create */
+    ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY = 1,
+    /** AHardwareBuffer */
+    ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
+};
+
 /*
  * A few useful constants
  */
@@ -367,12 +404,13 @@
  */
 ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
 
+#if __ANDROID_API__ >= 21
 /**
  * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor
  * of this type and wakeUp properties exists.
  */
-ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type,
-        bool wakeUp);
+ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp);
+#endif
 
 /**
  * Creates a new sensor event queue and associate it with a looper.
@@ -389,15 +427,108 @@
  */
 int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue);
 
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Create direct channel based on shared memory
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param fd      file descriptor representing a shared memory created by
+ *                {@link ASharedMemory_create}
+ * \param size    size to be used, must be less or equal to size of shared memory.
+ *
+ * \return a positive integer as a channel id to be used in
+ *         {@link ASensorManager_destroyDirectChannel} and
+ *         {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size);
+
+/**
+ * Create direct channel based on AHardwareBuffer
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param buffer  {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}.
+ * \param size    the intended size to be used, must be less or equal to size of buffer.
+ *
+ * \return a positive integer as a channel id to be used in
+ *         {@link ASensorManager_destroyDirectChannel} and
+ *         {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createHardwareBufferDirectChannel(
+        ASensorManager* manager, AHardwareBuffer const * buffer, size_t size);
+
+/**
+ * Destroy a direct channel
+ *
+ * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}.
+ * The buffer used for creating direct channel does not get destroyed with
+ * {@link ASensorManager_destroy} and has to be close or released separately.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param channelId channel id (a positive integer) returned from
+ *                  {@link ASensorManager_createSharedMemoryDirectChannel} or
+ *                  {@link ASensorManager_createHardwareBufferDirectChannel}.
+ */
+void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId);
+
+/**
+ * Configure direct report on channel
+ *
+ * Configure sensor direct report on a direct channel: set rate to value other than
+ * {@link ASENSOR_DIRECT_RATE_STOP} so that sensor event can be directly
+ * written into the shared memory region used for creating the buffer. It returns a positive token
+ * which can be used for identify sensor events from different sensors on success. Calling with rate
+ * {@link ASENSOR_DIRECT_RATE_STOP} will stop direct report of the sensor specified in the channel.
+ *
+ * To stop all active sensor direct report configured to a channel, set sensor to NULL and rate to
+ * {@link ASENSOR_DIRECT_RATE_STOP}.
+ *
+ * In order to successfully configure a direct report, the sensor has to support the specified rate
+ * and the channel type, which can be checked by {@link ASensor_getHighestDirectReportRateLevel} and
+ * {@link ASensor_isDirectChannelTypeSupported}, respectively.
+ *
+ * Example:
+ * \code{.cpp}
+ *      ASensorManager *manager = ...;
+ *      ASensor *sensor = ...;
+ *      int channelId = ...;
+ *
+ *      ASensorManager_configureDirectReport(
+ *              manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
+ * \endcode
+ *
+ * \param manager   the {@link ASensorManager} instance obtained from
+ *                  {@link ASensorManager_getInstanceForPackage}.
+ * \param sensor    a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate
+ *                  is {@link ASENSOR_DIRECT_RATE_STOP}, denoting stopping of all active sensor
+ *                  direct report.
+ * \param channelId channel id (a positive integer) returned from
+ *                  {@link ASensorManager_createSharedMemoryDirectChannel} or
+ *                  {@link ASensorManager_createHardwareBufferDirectChannel}.
+ *
+ * \return positive token for success or negative error code.
+ */
+int ASensorManager_configureDirectReport(
+        ASensorManager* manager, ASensor const* sensor, int channelId, int rate);
+#endif
 
 /*****************************************************************************/
 
 /**
  * Enable the selected sensor with a specified sampling period and max batch report latency.
  * Returns a negative error code on failure.
+ * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
  */
 int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
-        int32_t samplingPeriodUs, int maxBatchReportLatencyUs);
+        int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
 
 /**
  * Enable the selected sensor. Returns a negative error code on failure.
@@ -438,8 +569,7 @@
  *   ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
  *
  */
-ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
-                ASensorEvent* events, size_t count);
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
 
 
 /*****************************************************************************/
@@ -471,6 +601,7 @@
  */
 int ASensor_getMinDelay(ASensor const* sensor);
 
+#if __ANDROID_API__ >= 21
 /**
  * Returns the maximum size of batches for this sensor. Batches will often be
  * smaller, as the hardware fifo might be used for other sensors.
@@ -496,6 +627,30 @@
  * Returns true if this is a wake up sensor, false otherwise.
  */
 bool ASensor_isWakeUpSensor(ASensor const* sensor);
+#endif /* __ANDROID_API__ >= 21 */
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Test if sensor supports a certain type of direct channel.
+ *
+ * \param sensor  a {@link ASensor} to denote the sensor to be checked.
+ * \param channelType  Channel type constant, either
+ *                     {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
+ *                     or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}.
+ * \returns true if sensor supports the specified direct channel type.
+ */
+bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType);
+/**
+ * Get the highest direct rate level that a sensor support.
+ *
+ * \param sensor  a {@link ASensor} to denote the sensor to be checked.
+ *
+ * \return a ASENSOR_DIRECT_RATE_... enum denoting the highest rate level supported by the sensor.
+ *         If return value is {@link ASENSOR_DIRECT_RATE_STOP}, it means the sensor
+ *         does not support direct report.
+ */
+int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor);
+#endif
 
 #ifdef __cplusplus
 };
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
new file mode 100644
index 0000000..46d2f4b
--- /dev/null
+++ b/include/android/sharedmem.h
@@ -0,0 +1,119 @@
+/*
+ * 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.h
+ */
+
+#ifndef ANDROID_SHARED_MEMORY_H
+#define ANDROID_SHARED_MEMORY_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__
+
+/**
+ * Create a shared memory region.
+ *
+ * Create shared memory region and returns an file descriptor.  The resulting file descriptor can be
+ * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
+ * region can be restricted with {@link ASharedMemory_setProt}.
+ *
+ * Use close() to release the shared memory region.
+ *
+ * \param name an optional name.
+ * \param size size of the shared memory region
+ * \return file descriptor that denotes the shared memory; error code on failure.
+ */
+int ASharedMemory_create(const char *name, size_t size);
+
+/**
+ * Get the size of the shared memory region.
+ *
+ * \param fd file descriptor of the shared memory region
+ * \return size in bytes; 0 if fd is not a valid shared memory file descriptor.
+ */
+size_t ASharedMemory_getSize(int fd);
+
+/**
+ * Restrict access of shared memory region.
+ *
+ * This function restricts access of a shared memory region. Access can only be removed. The effect
+ * applies globally to all file descriptors in all processes across the system that refer to this
+ * shared memory region. Existing memory mapped regions are not affected.
+ *
+ * It is a common use case to create a shared memory region, map it read/write locally to intialize
+ * content, and then send the shared memory to another process with read only access. Code example
+ * as below (error handling omited).
+ *
+ *
+ *     int fd = ASharedMemory_create("memory", 128);
+ *
+ *     // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
+ *     char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ *
+ *     strcpy(buffer, "This is an example."); // trivially initialize content
+ *
+ *     // limit access to read only
+ *     ASharedMemory_setProt(fd, PROT_READ);
+ *
+ *     // share fd with another process here and the other process can only map with PROT_READ.
+ *
+ * \param fd   file descriptor of the shared memory region.
+ * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
+ *             updated access. Note access can only be removed, but not added back.
+ * \return 0 for success, error code on failure.
+ */
+int ASharedMemory_setProt(int fd, int prot);
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_SHARED_MEMORY_H
+
+/** @} */
diff --git a/include/android/trace.h b/include/android/trace.h
index e42e334..d3b1fb6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -14,16 +14,26 @@
  * limitations under the License.
  */
 
+/**
+ * @file trace.h
+ * @brief Writes trace events to the system trace buffer.
+ *
+ * These trace events can be collected and visualized using the Systrace tool.
+ * For information about using the Systrace tool, read <a href="https://developer.android.com/studio/profile/systrace.html">Analyzing UI Performance with Systrace</a>.
+ */
 
 #ifndef ANDROID_NATIVE_TRACE_H
 #define ANDROID_NATIVE_TRACE_H
 
 #include <stdbool.h>
+#include <sys/cdefs.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#if __ANDROID_API__ >= 23
+
 /**
  * Returns true if tracing is enabled. Use this signal to avoid expensive computation only necessary
  * when tracing is enabled.
@@ -48,6 +58,8 @@
  */
 void ATrace_endSection();
 
+#endif /* __ANDROID_API__ >= 23 */
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
new file mode 100644
index 0000000..009dc52
--- /dev/null
+++ b/include/audiomanager/AudioManager.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIOMANAGER_H
+#define ANDROID_AUDIOMANAGER_H
+
+namespace android {
+
+// must be kept in sync with definitions in AudioPlaybackConfiguration.java
+
+#define PLAYER_PIID_INVALID -1
+
+typedef enum {
+    PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
+    PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
+    PLAYER_TYPE_AAUDIO = 13,
+    PLAYER_TYPE_HW_SOURCE = 14,
+    PLAYER_TYPE_EXTERNAL_PROXY = 15,
+} player_type_t;
+
+typedef enum {
+    PLAYER_STATE_UNKNOWN  = -1,
+    PLAYER_STATE_RELEASED = 0,
+    PLAYER_STATE_IDLE     = 1,
+    PLAYER_STATE_STARTED  = 2,
+    PLAYER_STATE_PAUSED   = 3,
+    PLAYER_STATE_STOPPED  = 4,
+} player_state_t;
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
new file mode 100644
index 0000000..ce7804b
--- /dev/null
+++ b/include/audiomanager/IAudioManager.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IAUDIOMANAGER_H
+#define ANDROID_IAUDIOMANAGER_H
+
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <hardware/power.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioManager : public IInterface
+{
+public:
+    // These transaction IDs must be kept in sync with the method order from
+    // IAudioService.aidl.
+    enum {
+        // transaction IDs for the unsupported methods are commented out
+        /*
+        ADJUSTSUGGESTEDSTREAMVOLUME           = IBinder::FIRST_CALL_TRANSACTION,
+        ADJUSTSTREAMVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 1,
+        SETSTREAMVOLUME                       = IBinder::FIRST_CALL_TRANSACTION + 2,
+        ISSTREAMMUTE                          = IBinder::FIRST_CALL_TRANSACTION + 3,
+        FORCEREMOTESUBMIXFULLVOLUME           = IBinder::FIRST_CALL_TRANSACTION + 4,
+        ISMASTERMUTE                          = IBinder::FIRST_CALL_TRANSACTION + 5,
+        SETMASTERMUTE                         = IBinder::FIRST_CALL_TRANSACTION + 6,
+        GETSTREAMVOLUME                       = IBinder::FIRST_CALL_TRANSACTION + 7,
+        GETSTREAMMINVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 8,
+        GETSTREAMMAXVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 9,
+        GETLASTAUDIBLESTREAMVOLUME            = IBinder::FIRST_CALL_TRANSACTION + 10,
+        SETMICROPHONEMUTE                     = IBinder::FIRST_CALL_TRANSACTION + 11,
+        SETRINGERMODEEXTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 12,
+        SETRINGERMODEINTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 13,
+        GETRINGERMODEEXTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 14,
+        GETRINGERMODEINTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 15,
+        ISVALIDRINGERMODE                     = IBinder::FIRST_CALL_TRANSACTION + 16,
+        SETVIBRATESETTING                     = IBinder::FIRST_CALL_TRANSACTION + 17,
+        GETVIBRATESETTING                     = IBinder::FIRST_CALL_TRANSACTION + 18,
+        SHOULDVIBRATE                         = IBinder::FIRST_CALL_TRANSACTION + 19,
+        SETMODE                               = IBinder::FIRST_CALL_TRANSACTION + 20,
+        GETMODE                               = IBinder::FIRST_CALL_TRANSACTION + 21,
+        PLAYSOUNDEFFECT                       = IBinder::FIRST_CALL_TRANSACTION + 22,
+        PLAYSOUNDEFFECTVOLUME                 = IBinder::FIRST_CALL_TRANSACTION + 23,
+        LOADSOUNDEFFECTS                      = IBinder::FIRST_CALL_TRANSACTION + 24,
+        UNLOADSOUNDEFFECTS                    = IBinder::FIRST_CALL_TRANSACTION + 25,
+        RELOADAUDIOSETTINGS                   = IBinder::FIRST_CALL_TRANSACTION + 26,
+        AVRCPSUPPORTSABSOLUTEVOLUME           = IBinder::FIRST_CALL_TRANSACTION + 27,
+        SETSPEAKERPHONEON                     = IBinder::FIRST_CALL_TRANSACTION + 28,
+        ISSPEAKERPHONEON                      = IBinder::FIRST_CALL_TRANSACTION + 29,
+        SETBLUETOOTHSCOON                     = IBinder::FIRST_CALL_TRANSACTION + 30,
+        ISBLUETOOTHSCOON                      = IBinder::FIRST_CALL_TRANSACTION + 31,
+        SETBLUETOOTHA2DPON                    = IBinder::FIRST_CALL_TRANSACTION + 32,
+        ISBLUETOOTHA2DPON                     = IBinder::FIRST_CALL_TRANSACTION + 33,
+        REQUESTAUDIOFOCUS                     = IBinder::FIRST_CALL_TRANSACTION + 34,
+        ABANDONAUDIOFOCUS                     = IBinder::FIRST_CALL_TRANSACTION + 35,
+        UNREGISTERAUDIOFOCUSCLIENT            = IBinder::FIRST_CALL_TRANSACTION + 36,
+        GETCURRENTAUDIOFOCUS                  = IBinder::FIRST_CALL_TRANSACTION + 37,
+        STARTBLUETOOTHSCO                     = IBinder::FIRST_CALL_TRANSACTION + 38,
+        STARTBLUETOOTHSCOVIRTUALCALL          = IBinder::FIRST_CALL_TRANSACTION + 39,
+        STOPBLUETOOTHSCO                      = IBinder::FIRST_CALL_TRANSACTION + 40,
+        FORCEVOLUMECONTROLSTREAM              = IBinder::FIRST_CALL_TRANSACTION + 41,
+        SETRINGTONEPLAYER                     = IBinder::FIRST_CALL_TRANSACTION + 42,
+        GETRINGTONEPLAYER                     = IBinder::FIRST_CALL_TRANSACTION + 43,
+        GETUISOUNDSSTREAMTYPE                 = IBinder::FIRST_CALL_TRANSACTION + 44,
+        SETWIREDDEVICECONNECTIONSTATE         = IBinder::FIRST_CALL_TRANSACTION + 45,
+        SETBLUETOOTHA2DPDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 46,
+        HANDLEBLUETOOTHA2DPDEVICECONFIGCHANGE = IBinder::FIRST_CALL_TRANSACTION + 47,
+        STARTWATCHINGROUTES                   = IBinder::FIRST_CALL_TRANSACTION + 48,
+        ISCAMERASOUNDFORCED                   = IBinder::FIRST_CALL_TRANSACTION + 49,
+        SETVOLUMECONTROLLER                   = IBinder::FIRST_CALL_TRANSACTION + 50,
+        NOTIFYVOLUMECONTROLLERVISIBLE         = IBinder::FIRST_CALL_TRANSACTION + 51,
+        ISSTREAMAFFECTEDBYRINGERMODE          = IBinder::FIRST_CALL_TRANSACTION + 52,
+        ISSTREAMAFFECTEDBYMUTE                = IBinder::FIRST_CALL_TRANSACTION + 53,
+        DISABLESAFEMEDIAVOLUME                = IBinder::FIRST_CALL_TRANSACTION + 54,
+        SETHDMISYSTEMAUDIOSUPPORTED           = IBinder::FIRST_CALL_TRANSACTION + 55,
+        ISHDMISYSTEMAUDIOSUPPORTED            = IBinder::FIRST_CALL_TRANSACTION + 56,
+        REGISTERAUDIOPOLICY                   = IBinder::FIRST_CALL_TRANSACTION + 57,
+        UNREGISTERAUDIOPOLICYASYNC            = IBinder::FIRST_CALL_TRANSACTION + 58,
+        SETFOCUSPROPERTIESFORPOLICY           = IBinder::FIRST_CALL_TRANSACTION + 59,
+        SETVOLUMEPOLICY                       = IBinder::FIRST_CALL_TRANSACTION + 60,
+        REGISTERRECORDINGCALLBACK             = IBinder::FIRST_CALL_TRANSACTION + 61,
+        UNREGISTERRECORDINGCALLBACK           = IBinder::FIRST_CALL_TRANSACTION + 62,
+        GETACTIVERECORDINGCONFIGURATIONS      = IBinder::FIRST_CALL_TRANSACTION + 63,
+        REGISTERPLAYBACKCALLBACK              = IBinder::FIRST_CALL_TRANSACTION + 64,
+        UNREGISTERPLAYBACKCALLBACK            = IBinder::FIRST_CALL_TRANSACTION + 65,
+        GETACTIVEPLAYBACKCONFIGURATIONS       = IBinder::FIRST_CALL_TRANSACTION + 66,
+        */
+
+        TRACK_PLAYER                          = IBinder::FIRST_CALL_TRANSACTION + 67,
+        PLAYER_ATTRIBUTES                     = IBinder::FIRST_CALL_TRANSACTION + 68,
+        PLAYER_EVENT                          = IBinder::FIRST_CALL_TRANSACTION + 69,
+        RELEASE_PLAYER                        = IBinder::FIRST_CALL_TRANSACTION + 70,
+
+        /*
+        DISABLE_RINGTONE_SYNC                 = IBinder::FIRST_CALL_TRANSACTION + 71,
+        */
+    };
+
+    DECLARE_META_INTERFACE(AudioManager)
+
+    // The parcels created by these methods must be kept in sync with the
+    // corresponding methods from IAudioService.aidl and objects it imports.
+    virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
+                audio_content_type_t content, const sp<IBinder>& player) = 0;
+    /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
+                audio_content_type_t content)= 0;
+    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+    /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOMANAGER_H
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
new file mode 100644
index 0000000..de5c1c7
--- /dev/null
+++ b/include/audiomanager/IPlayer.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IPLAYER_H
+#define ANDROID_IPLAYER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <media/VolumeShaper.h>
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IPlayer : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(Player);
+
+    virtual void start() = 0;
+
+    virtual void pause() = 0;
+
+    virtual void stop() = 0;
+
+    virtual void setVolume(float vol) = 0;
+
+    virtual void setPan(float pan) = 0;
+
+    virtual void setStartDelayMs(int delayMs) = 0;
+
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnPlayer : public BnInterface<IPlayer>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPLAYER_H
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index b399905..80ab7f3 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,33 +24,17 @@
 
 namespace android {
 
-// must be kept in sync with definitions in BatteryManager.java
-enum {
-    BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant
-    BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant
-    BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant
-    BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant
-    BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant
-};
+#include "BatteryServiceConstants.h"
 
-// must be kept in sync with definitions in BatteryManager.java
+// must be kept in sync with definitions in
+// frameworks/base/core/java/android/os/BatteryManager.java
 enum {
-    BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant
-    BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant
-    BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant
-    BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant
-    BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant
-    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant
-    BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant
-};
-
-// must be kept in sync with definitions in BatteryProperty.java
-enum {
-    BATTERY_PROP_CHARGE_COUNTER = 1, // equals BatteryProperty.CHARGE_COUNTER constant
-    BATTERY_PROP_CURRENT_NOW = 2, // equals BatteryProperty.CURRENT_NOW constant
-    BATTERY_PROP_CURRENT_AVG = 3, // equals BatteryProperty.CURRENT_AVG constant
-    BATTERY_PROP_CAPACITY = 4, // equals BatteryProperty.CAPACITY constant
-    BATTERY_PROP_ENERGY_COUNTER = 5, // equals BatteryProperty.ENERGY_COUNTER constant
+    BATTERY_PROP_CHARGE_COUNTER = 1, // equals BATTERY_PROPERTY_CHARGE_COUNTER
+    BATTERY_PROP_CURRENT_NOW = 2, // equals BATTERY_PROPERTY_CURRENT_NOW
+    BATTERY_PROP_CURRENT_AVG = 3, // equals BATTERY_PROPERTY_CURRENT_AVERAGE
+    BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
+    BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
+    BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
 };
 
 struct BatteryProperties {
diff --git a/include/batteryservice/BatteryServiceConstants.h b/include/batteryservice/BatteryServiceConstants.h
new file mode 100644
index 0000000..8a90a12
--- /dev/null
+++ b/include/batteryservice/BatteryServiceConstants.h
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+    BATTERY_STATUS_UNKNOWN = 1,
+    BATTERY_STATUS_CHARGING = 2,
+    BATTERY_STATUS_DISCHARGING = 3,
+    BATTERY_STATUS_NOT_CHARGING = 4,
+    BATTERY_STATUS_FULL = 5,
+};
+
+enum {
+    BATTERY_HEALTH_UNKNOWN = 1,
+    BATTERY_HEALTH_GOOD = 2,
+    BATTERY_HEALTH_OVERHEAT = 3,
+    BATTERY_HEALTH_DEAD = 4,
+    BATTERY_HEALTH_OVER_VOLTAGE = 5,
+    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
+    BATTERY_HEALTH_COLD = 7,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/batteryservice/IBatteryPropertiesListener.h b/include/batteryservice/IBatteryPropertiesListener.h
index b02d8e9..b226dd6 100644
--- a/include/batteryservice/IBatteryPropertiesListener.h
+++ b/include/batteryservice/IBatteryPropertiesListener.h
@@ -33,13 +33,19 @@
 
 class IBatteryPropertiesListener : public IInterface {
 public:
-    DECLARE_META_INTERFACE(BatteryPropertiesListener);
+    DECLARE_META_INTERFACE(BatteryPropertiesListener)
 
     virtual void batteryPropertiesChanged(struct BatteryProperties props) = 0;
 };
 
 // ----------------------------------------------------------------------------
 
+class BnBatteryPropertiesListener: public BnInterface<IBatteryPropertiesListener> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
 }; // namespace android
 
 #endif // ANDROID_IBATTERYPROPERTIESLISTENER_H
diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/include/batteryservice/IBatteryPropertiesRegistrar.h
index eca075d..a7dbea6 100644
--- a/include/batteryservice/IBatteryPropertiesRegistrar.h
+++ b/include/batteryservice/IBatteryPropertiesRegistrar.h
@@ -27,15 +27,17 @@
     REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
     UNREGISTER_LISTENER,
     GET_PROPERTY,
+    SCHEDULE_UPDATE,
 };
 
 class IBatteryPropertiesRegistrar : public IInterface {
 public:
-    DECLARE_META_INTERFACE(BatteryPropertiesRegistrar);
+    DECLARE_META_INTERFACE(BatteryPropertiesRegistrar)
 
     virtual void registerListener(const sp<IBatteryPropertiesListener>& listener) = 0;
     virtual void unregisterListener(const sp<IBatteryPropertiesListener>& listener) = 0;
     virtual status_t getProperty(int id, struct BatteryProperty *val) = 0;
+    virtual void scheduleUpdate() = 0;
 };
 
 class BnBatteryPropertiesRegistrar : public BnInterface<IBatteryPropertiesRegistrar> {
diff --git a/include/binder b/include/binder
new file mode 120000
index 0000000..35a022a
--- /dev/null
+++ b/include/binder
@@ -0,0 +1 @@
+../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
deleted file mode 100644
index 042927c..0000000
--- a/include/binder/AppOpsManager.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_APP_OPS_MANAGER_H
-#define ANDROID_APP_OPS_MANAGER_H
-
-#include <binder/IAppOpsService.h>
-
-#include <utils/threads.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class AppOpsManager
-{
-public:
-    enum {
-        MODE_ALLOWED = IAppOpsService::MODE_ALLOWED,
-        MODE_IGNORED = IAppOpsService::MODE_IGNORED,
-        MODE_ERRORED = IAppOpsService::MODE_ERRORED
-    };
-
-    enum {
-        OP_NONE = -1,
-        OP_COARSE_LOCATION = 0,
-        OP_FINE_LOCATION = 1,
-        OP_GPS = 2,
-        OP_VIBRATE = 3,
-        OP_READ_CONTACTS = 4,
-        OP_WRITE_CONTACTS = 5,
-        OP_READ_CALL_LOG = 6,
-        OP_WRITE_CALL_LOG = 7,
-        OP_READ_CALENDAR = 8,
-        OP_WRITE_CALENDAR = 9,
-        OP_WIFI_SCAN = 10,
-        OP_POST_NOTIFICATION = 11,
-        OP_NEIGHBORING_CELLS = 12,
-        OP_CALL_PHONE = 13,
-        OP_READ_SMS = 14,
-        OP_WRITE_SMS = 15,
-        OP_RECEIVE_SMS = 16,
-        OP_RECEIVE_EMERGECY_SMS = 17,
-        OP_RECEIVE_MMS = 18,
-        OP_RECEIVE_WAP_PUSH = 19,
-        OP_SEND_SMS = 20,
-        OP_READ_ICC_SMS = 21,
-        OP_WRITE_ICC_SMS = 22,
-        OP_WRITE_SETTINGS = 23,
-        OP_SYSTEM_ALERT_WINDOW = 24,
-        OP_ACCESS_NOTIFICATIONS = 25,
-        OP_CAMERA = 26,
-        OP_RECORD_AUDIO = 27,
-        OP_PLAY_AUDIO = 28,
-        OP_READ_CLIPBOARD = 29,
-        OP_WRITE_CLIPBOARD = 30,
-        OP_TAKE_MEDIA_BUTTONS = 31,
-        OP_TAKE_AUDIO_FOCUS = 32,
-        OP_AUDIO_MASTER_VOLUME = 33,
-        OP_AUDIO_VOICE_VOLUME = 34,
-        OP_AUDIO_RING_VOLUME = 35,
-        OP_AUDIO_MEDIA_VOLUME = 36,
-        OP_AUDIO_ALARM_VOLUME = 37,
-        OP_AUDIO_NOTIFICATION_VOLUME = 38,
-        OP_AUDIO_BLUETOOTH_VOLUME = 39,
-        OP_WAKE_LOCK = 40,
-        OP_MONITOR_LOCATION = 41,
-        OP_MONITOR_HIGH_POWER_LOCATION = 42,
-        OP_GET_USAGE_STATS = 43,
-        OP_MUTE_MICROPHONE = 44,
-        OP_TOAST_WINDOW = 45,
-        OP_PROJECT_MEDIA = 46,
-        OP_ACTIVATE_VPN = 47,
-        OP_WRITE_WALLPAPER = 48,
-        OP_ASSIST_STRUCTURE = 49,
-        OP_ASSIST_SCREENSHOT = 50,
-        OP_READ_PHONE_STATE = 51,
-        OP_ADD_VOICEMAIL = 52,
-        OP_USE_SIP = 53,
-        OP_PROCESS_OUTGOING_CALLS = 54,
-        OP_USE_FINGERPRINT = 55,
-        OP_BODY_SENSORS = 56
-    };
-
-    AppOpsManager();
-
-    int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
-    int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
-    int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage);
-    void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
-    void startWatchingMode(int32_t op, const String16& packageName,
-            const sp<IAppOpsCallback>& callback);
-    void stopWatchingMode(const sp<IAppOpsCallback>& callback);
-    int32_t permissionToOpCode(const String16& permission);
-
-private:
-    Mutex mLock;
-    sp<IAppOpsService> mService;
-
-    sp<IAppOpsService> getService();
-};
-
-
-}; // namespace android
-// ---------------------------------------------------------------------------
-#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
deleted file mode 100644
index f849fd4..0000000
--- a/include/binder/Binder.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_BINDER_H
-#define ANDROID_BINDER_H
-
-#include <atomic>
-#include <stdint.h>
-#include <binder/IBinder.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder : public IBinder
-{
-public:
-                        BBinder();
-
-    virtual const String16& getInterfaceDescriptor() const;
-    virtual bool        isBinderAlive() const;
-    virtual status_t    pingBinder();
-    virtual status_t    dump(int fd, const Vector<String16>& args);
-
-    virtual status_t    transact(   uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-
-    virtual status_t    linkToDeath(const sp<DeathRecipient>& recipient,
-                                    void* cookie = NULL,
-                                    uint32_t flags = 0);
-
-    virtual status_t    unlinkToDeath(  const wp<DeathRecipient>& recipient,
-                                        void* cookie = NULL,
-                                        uint32_t flags = 0,
-                                        wp<DeathRecipient>* outRecipient = NULL);
-
-    virtual void        attachObject(   const void* objectID,
-                                        void* object,
-                                        void* cleanupCookie,
-                                        object_cleanup_func func);
-    virtual void*       findObject(const void* objectID) const;
-    virtual void        detachObject(const void* objectID);
-
-    virtual BBinder*    localBinder();
-
-protected:
-    virtual             ~BBinder();
-
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-
-private:
-                        BBinder(const BBinder& o);
-            BBinder&    operator=(const BBinder& o);
-
-    class Extras;
-
-    std::atomic<Extras*> mExtras;
-            void*       mReserved0;
-};
-
-// ---------------------------------------------------------------------------
-
-class BpRefBase : public virtual RefBase
-{
-protected:
-                            BpRefBase(const sp<IBinder>& o);
-    virtual                 ~BpRefBase();
-    virtual void            onFirstRef();
-    virtual void            onLastStrongRef(const void* id);
-    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
-
-    inline  IBinder*        remote()                { return mRemote; }
-    inline  IBinder*        remote() const          { return mRemote; }
-
-private:
-                            BpRefBase(const BpRefBase& o);
-    BpRefBase&              operator=(const BpRefBase& o);
-
-    IBinder* const          mRemote;
-    RefBase::weakref_type*  mRefs;
-    std::atomic<int32_t>    mState;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BINDER_H
diff --git a/include/binder/IAppOpsCallback.h b/include/binder/IAppOpsCallback.h
deleted file mode 100644
index 7f8eb01..0000000
--- a/include/binder/IAppOpsCallback.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_IAPP_OPS_CALLBACK_H
-#define ANDROID_IAPP_OPS_CALLBACK_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsCallback : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(AppOpsCallback);
-
-    virtual void opChanged(int32_t op, const String16& packageName) = 0;
-
-    enum {
-        OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAPP_OPS_CALLBACK_H
-
diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h
deleted file mode 100644
index cd81efa..0000000
--- a/include/binder/IAppOpsService.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_IAPP_OPS_SERVICE_H
-#define ANDROID_IAPP_OPS_SERVICE_H
-
-#include <binder/IAppOpsCallback.h>
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsService : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(AppOpsService);
-
-    virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
-    virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) = 0;
-    virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) = 0;
-    virtual void startWatchingMode(int32_t op, const String16& packageName,
-            const sp<IAppOpsCallback>& callback) = 0;
-    virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
-    virtual int32_t permissionToOpCode(const String16& permission) = 0;
-
-    enum {
-        CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
-        START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
-        FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
-        START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
-        STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
-        GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
-        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
-    };
-
-    enum {
-        MODE_ALLOWED = 0,
-        MODE_IGNORED = 1,
-        MODE_ERRORED = 2
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsService : public BnInterface<IAppOpsService>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/include/binder/IBatteryStats.h b/include/binder/IBatteryStats.h
deleted file mode 100644
index 5f38186..0000000
--- a/include/binder/IBatteryStats.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IBATTERYSTATS_H
-#define ANDROID_IBATTERYSTATS_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IBatteryStats : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(BatteryStats);
-
-    virtual void noteStartSensor(int uid, int sensor) = 0;
-    virtual void noteStopSensor(int uid, int sensor) = 0;
-    virtual void noteStartVideo(int uid) = 0;
-    virtual void noteStopVideo(int uid) = 0;
-    virtual void noteStartAudio(int uid) = 0;
-    virtual void noteStopAudio(int uid) = 0;
-    virtual void noteResetVideo() = 0;
-    virtual void noteResetAudio() = 0;
-    virtual void noteFlashlightOn(int uid) = 0;
-    virtual void noteFlashlightOff(int uid) = 0;
-    virtual void noteStartCamera(int uid) = 0;
-    virtual void noteStopCamera(int uid) = 0;
-    virtual void noteResetCamera() = 0;
-    virtual void noteResetFlashlight() = 0;
-
-    enum {
-        NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        NOTE_STOP_SENSOR_TRANSACTION,
-        NOTE_START_VIDEO_TRANSACTION,
-        NOTE_STOP_VIDEO_TRANSACTION,
-        NOTE_START_AUDIO_TRANSACTION,
-        NOTE_STOP_AUDIO_TRANSACTION,
-        NOTE_RESET_VIDEO_TRANSACTION,
-        NOTE_RESET_AUDIO_TRANSACTION,
-        NOTE_FLASHLIGHT_ON_TRANSACTION,
-        NOTE_FLASHLIGHT_OFF_TRANSACTION,
-        NOTE_START_CAMERA_TRANSACTION,
-        NOTE_STOP_CAMERA_TRANSACTION,
-        NOTE_RESET_CAMERA_TRANSACTION,
-        NOTE_RESET_FLASHLIGHT_TRANSACTION
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnBatteryStats : public BnInterface<IBatteryStats>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IBATTERYSTATS_H
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
deleted file mode 100644
index 5f1e87c..0000000
--- a/include/binder/IBinder.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IBINDER_H
-#define ANDROID_IBINDER_H
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-
-// linux/binder.h already defines this, but we can't just include it from there
-// because there are host builds that include this file.
-#ifndef B_PACK_CHARS
-#define B_PACK_CHARS(c1, c2, c3, c4) \
-    ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
-#endif  // B_PACK_CHARS
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder;
-class BpBinder;
-class IInterface;
-class Parcel;
-class IResultReceiver;
-
-/**
- * Base class and low-level protocol for a remotable object.
- * You can derive from this class to create an object for which other
- * processes can hold references to it.  Communication between processes
- * (method calls, property get and set) is down through a low-level
- * protocol implemented on top of the transact() API.
- */
-class IBinder : public virtual RefBase
-{
-public:
-    enum {
-        FIRST_CALL_TRANSACTION  = 0x00000001,
-        LAST_CALL_TRANSACTION   = 0x00ffffff,
-
-        PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
-        DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
-        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
-        INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
-        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),
-
-        // Corresponds to TF_ONE_WAY -- an asynchronous call.
-        FLAG_ONEWAY             = 0x00000001
-    };
-
-                          IBinder();
-
-    /**
-     * Check if this IBinder implements the interface named by
-     * @a descriptor.  If it does, the base pointer to it is returned,
-     * which you can safely static_cast<> to the concrete C++ interface.
-     */
-    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
-
-    /**
-     * Return the canonical name of the interface provided by this IBinder
-     * object.
-     */
-    virtual const String16& getInterfaceDescriptor() const = 0;
-
-    virtual bool            isBinderAlive() const = 0;
-    virtual status_t        pingBinder() = 0;
-    virtual status_t        dump(int fd, const Vector<String16>& args) = 0;
-    static  status_t        shellCommand(const sp<IBinder>& target, int in, int out, int err,
-                                         Vector<String16>& args,
-                                         const sp<IResultReceiver>& resultReceiver);
-
-    virtual status_t        transact(   uint32_t code,
-                                        const Parcel& data,
-                                        Parcel* reply,
-                                        uint32_t flags = 0) = 0;
-
-    class DeathRecipient : public virtual RefBase
-    {
-    public:
-        virtual void binderDied(const wp<IBinder>& who) = 0;
-    };
-
-    /**
-     * Register the @a recipient for a notification if this binder
-     * goes away.  If this binder object unexpectedly goes away
-     * (typically because its hosting process has been killed),
-     * then DeathRecipient::binderDied() will be called with a reference
-     * to this.
-     *
-     * The @a cookie is optional -- if non-NULL, it should be a
-     * memory address that you own (that is, you know it is unique).
-     *
-     * @note You will only receive death notifications for remote binders,
-     * as local binders by definition can't die without you dying as well.
-     * Trying to use this function on a local binder will result in an
-     * INVALID_OPERATION code being returned and nothing happening.
-     *
-     * @note This link always holds a weak reference to its recipient.
-     *
-     * @note You will only receive a weak reference to the dead
-     * binder.  You should not try to promote this to a strong reference.
-     * (Nor should you need to, as there is nothing useful you can
-     * directly do with it now that it has passed on.)
-     */
-    virtual status_t        linkToDeath(const sp<DeathRecipient>& recipient,
-                                        void* cookie = NULL,
-                                        uint32_t flags = 0) = 0;
-
-    /**
-     * Remove a previously registered death notification.
-     * The @a recipient will no longer be called if this object
-     * dies.  The @a cookie is optional.  If non-NULL, you can
-     * supply a NULL @a recipient, and the recipient previously
-     * added with that cookie will be unlinked.
-     */
-    virtual status_t        unlinkToDeath(  const wp<DeathRecipient>& recipient,
-                                            void* cookie = NULL,
-                                            uint32_t flags = 0,
-                                            wp<DeathRecipient>* outRecipient = NULL) = 0;
-
-    virtual bool            checkSubclass(const void* subclassID) const;
-
-    typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
-
-    virtual void            attachObject(   const void* objectID,
-                                            void* object,
-                                            void* cleanupCookie,
-                                            object_cleanup_func func) = 0;
-    virtual void*           findObject(const void* objectID) const = 0;
-    virtual void            detachObject(const void* objectID) = 0;
-
-    virtual BBinder*        localBinder();
-    virtual BpBinder*       remoteBinder();
-
-protected:
-    virtual          ~IBinder();
-
-private:
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IBINDER_H
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
deleted file mode 100644
index 4ce3613..0000000
--- a/include/binder/IInterface.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_IINTERFACE_H
-#define ANDROID_IINTERFACE_H
-
-#include <binder/Binder.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IInterface : public virtual RefBase
-{
-public:
-            IInterface();
-            static sp<IBinder>  asBinder(const IInterface*);
-            static sp<IBinder>  asBinder(const sp<IInterface>&);
-
-protected:
-    virtual                     ~IInterface();
-    virtual IBinder*            onAsBinder() = 0;
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
-{
-    return INTERFACE::asInterface(obj);
-}
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BnInterface : public INTERFACE, public BBinder
-{
-public:
-    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
-    virtual const String16&     getInterfaceDescriptor() const;
-
-protected:
-    virtual IBinder*            onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BpInterface : public INTERFACE, public BpRefBase
-{
-public:
-                                BpInterface(const sp<IBinder>& remote);
-
-protected:
-    virtual IBinder*            onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-#define DECLARE_META_INTERFACE(INTERFACE)                               \
-    static const android::String16 descriptor;                          \
-    static android::sp<I##INTERFACE> asInterface(                       \
-            const android::sp<android::IBinder>& obj);                  \
-    virtual const android::String16& getInterfaceDescriptor() const;    \
-    I##INTERFACE();                                                     \
-    virtual ~I##INTERFACE();                                            \
-
-
-#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
-    const android::String16 I##INTERFACE::descriptor(NAME);             \
-    const android::String16&                                            \
-            I##INTERFACE::getInterfaceDescriptor() const {              \
-        return I##INTERFACE::descriptor;                                \
-    }                                                                   \
-    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
-            const android::sp<android::IBinder>& obj)                   \
-    {                                                                   \
-        android::sp<I##INTERFACE> intr;                                 \
-        if (obj != NULL) {                                              \
-            intr = static_cast<I##INTERFACE*>(                          \
-                obj->queryLocalInterface(                               \
-                        I##INTERFACE::descriptor).get());               \
-            if (intr == NULL) {                                         \
-                intr = new Bp##INTERFACE(obj);                          \
-            }                                                           \
-        }                                                               \
-        return intr;                                                    \
-    }                                                                   \
-    I##INTERFACE::I##INTERFACE() { }                                    \
-    I##INTERFACE::~I##INTERFACE() { }                                   \
-
-
-#define CHECK_INTERFACE(interface, data, reply)                         \
-    if (!data.checkInterface(this)) { return PERMISSION_DENIED; }       \
-
-
-// ----------------------------------------------------------------------
-// No user-serviceable parts after this...
-
-template<typename INTERFACE>
-inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
-        const String16& _descriptor)
-{
-    if (_descriptor == INTERFACE::descriptor) return this;
-    return NULL;
-}
-
-template<typename INTERFACE>
-inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
-{
-    return INTERFACE::getInterfaceDescriptor();
-}
-
-template<typename INTERFACE>
-IBinder* BnInterface<INTERFACE>::onAsBinder()
-{
-    return this;
-}
-
-template<typename INTERFACE>
-inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
-    : BpRefBase(remote)
-{
-}
-
-template<typename INTERFACE>
-inline IBinder* BpInterface<INTERFACE>::onAsBinder()
-{
-    return remote();
-}
-    
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IINTERFACE_H
diff --git a/include/binder/IMediaResourceMonitor.h b/include/binder/IMediaResourceMonitor.h
deleted file mode 100644
index c671f7a..0000000
--- a/include/binder/IMediaResourceMonitor.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IMediaResourceMonitor : public IInterface {
-public:
-    DECLARE_META_INTERFACE(MediaResourceMonitor);
-
-    // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
-    enum {
-        TYPE_VIDEO_CODEC = 0,
-        TYPE_AUDIO_CODEC = 1,
-    };
-
-    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
-
-    enum {
-        NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
-public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-            uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
deleted file mode 100644
index 2d0db00..0000000
--- a/include/binder/IMemory.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IMEMORY_H
-#define ANDROID_IMEMORY_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IMemoryHeap : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(MemoryHeap);
-
-    // flags returned by getFlags()
-    enum {
-        READ_ONLY   = 0x00000001
-    };
-
-    virtual int         getHeapID() const = 0;
-    virtual void*       getBase() const = 0;
-    virtual size_t      getSize() const = 0;
-    virtual uint32_t    getFlags() const = 0;
-    virtual uint32_t    getOffset() const = 0;
-
-    // these are there just for backward source compatibility
-    int32_t heapID() const { return getHeapID(); }
-    void*   base() const  { return getBase(); }
-    size_t  virtualSize() const { return getSize(); }
-};
-
-class BnMemoryHeap : public BnInterface<IMemoryHeap>
-{
-public:
-    virtual status_t onTransact( 
-            uint32_t code,
-            const Parcel& data,
-            Parcel* reply,
-            uint32_t flags = 0);
-    
-    BnMemoryHeap();
-protected:
-    virtual ~BnMemoryHeap();
-};
-
-// ----------------------------------------------------------------------------
-
-class IMemory : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(Memory);
-
-    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
-
-    // helpers
-    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
-    void* pointer() const;
-    size_t size() const;
-    ssize_t offset() const;
-};
-
-class BnMemory : public BnInterface<IMemory>
-{
-public:
-    virtual status_t onTransact(
-            uint32_t code,
-            const Parcel& data,
-            Parcel* reply,
-            uint32_t flags = 0);
-
-    BnMemory();
-protected:
-    virtual ~BnMemory();
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IMEMORY_H
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
deleted file mode 100644
index 7b826d6..0000000
--- a/include/binder/IPCThreadState.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IPC_THREAD_STATE_H
-#define ANDROID_IPC_THREAD_STATE_H
-
-#include <utils/Errors.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <utils/Vector.h>
-
-#if defined(_WIN32)
-typedef  int  uid_t;
-#endif
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IPCThreadState
-{
-public:
-    static  IPCThreadState*     self();
-    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
-    
-            sp<ProcessState>    process();
-            
-            status_t            clearLastError();
-
-            pid_t               getCallingPid() const;
-            uid_t               getCallingUid() const;
-
-            void                setStrictModePolicy(int32_t policy);
-            int32_t             getStrictModePolicy() const;
-
-            void                setLastTransactionBinderFlags(int32_t flags);
-            int32_t             getLastTransactionBinderFlags() const;
-
-            int64_t             clearCallingIdentity();
-            void                restoreCallingIdentity(int64_t token);
-            
-            int                 setupPolling(int* fd);
-            status_t            handlePolledCommands();
-            void                flushCommands();
-
-            void                joinThreadPool(bool isMain = true);
-            
-            // Stop the local process.
-            void                stopProcess(bool immediate = true);
-            
-            status_t            transact(int32_t handle,
-                                         uint32_t code, const Parcel& data,
-                                         Parcel* reply, uint32_t flags);
-
-            void                incStrongHandle(int32_t handle);
-            void                decStrongHandle(int32_t handle);
-            void                incWeakHandle(int32_t handle);
-            void                decWeakHandle(int32_t handle);
-            status_t            attemptIncStrongHandle(int32_t handle);
-    static  void                expungeHandle(int32_t handle, IBinder* binder);
-            status_t            requestDeathNotification(   int32_t handle,
-                                                            BpBinder* proxy); 
-            status_t            clearDeathNotification( int32_t handle,
-                                                        BpBinder* proxy); 
-
-    static  void                shutdown();
-
-    // Call this to disable switching threads to background scheduling when
-    // receiving incoming IPC calls.  This is specifically here for the
-    // Android system process, since it expects to have background apps calling
-    // in to it but doesn't want to acquire locks in its services while in
-    // the background.
-    static  void                disableBackgroundScheduling(bool disable);
-
-            // Call blocks until the number of executing binder threads is less than
-            // the maximum number of binder threads threads allowed for this process.
-            void                blockUntilThreadAvailable();
-
-private:
-                                IPCThreadState();
-                                ~IPCThreadState();
-
-            status_t            sendReply(const Parcel& reply, uint32_t flags);
-            status_t            waitForResponse(Parcel *reply,
-                                                status_t *acquireResult=NULL);
-            status_t            talkWithDriver(bool doReceive=true);
-            status_t            writeTransactionData(int32_t cmd,
-                                                     uint32_t binderFlags,
-                                                     int32_t handle,
-                                                     uint32_t code,
-                                                     const Parcel& data,
-                                                     status_t* statusBuffer);
-            status_t            getAndExecuteCommand();
-            status_t            executeCommand(int32_t command);
-            void                processPendingDerefs();
-
-            void                clearCaller();
-
-    static  void                threadDestructor(void *st);
-    static  void                freeBuffer(Parcel* parcel,
-                                           const uint8_t* data, size_t dataSize,
-                                           const binder_size_t* objects, size_t objectsSize,
-                                           void* cookie);
-    
-    const   sp<ProcessState>    mProcess;
-            Vector<BBinder*>    mPendingStrongDerefs;
-            Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-
-            Parcel              mIn;
-            Parcel              mOut;
-            status_t            mLastError;
-            pid_t               mCallingPid;
-            uid_t               mCallingUid;
-            int32_t             mStrictModePolicy;
-            int32_t             mLastTransactionBinderFlags;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/include/binder/IPermissionController.h b/include/binder/IPermissionController.h
deleted file mode 100644
index 4e5fb34..0000000
--- a/include/binder/IPermissionController.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_IPERMISSION_CONTROLLER_H
-#define ANDROID_IPERMISSION_CONTROLLER_H
-
-#include <binder/IInterface.h>
-#include <stdlib.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IPermissionController : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(PermissionController);
-
-    virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) = 0;
-
-    virtual void getPackagesForUid(const uid_t uid, Vector<String16> &packages) = 0;
-
-    virtual bool isRuntimePermission(const String16& permission) = 0;
-
-    enum {
-        CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1,
-        IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnPermissionController : public BnInterface<IPermissionController>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPERMISSION_CONTROLLER_H
-
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
deleted file mode 100644
index 69dc9a7..0000000
--- a/include/binder/IProcessInfoService.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_I_PROCESS_INFO_SERVICE_H
-#define ANDROID_I_PROCESS_INFO_SERVICE_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IProcessInfoService : public IInterface {
-public:
-    DECLARE_META_INTERFACE(ProcessInfoService);
-
-    virtual status_t    getProcessStatesFromPids( size_t length,
-                                                  /*in*/ int32_t* pids,
-                                                  /*out*/ int32_t* states) = 0;
-
-    virtual status_t    getProcessStatesAndOomScoresFromPids( size_t length,
-                                                  /*in*/ int32_t* pids,
-                                                  /*out*/ int32_t* states,
-                                                  /*out*/ int32_t* scores) = 0;
-
-    enum {
-        GET_PROCESS_STATES_FROM_PIDS = IBinder::FIRST_CALL_TRANSACTION,
-        GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS,
-    };
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/include/binder/IResultReceiver.h b/include/binder/IResultReceiver.h
deleted file mode 100644
index 02dc6a6..0000000
--- a/include/binder/IResultReceiver.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_IRESULT_RECEIVER_H
-#define ANDROID_IRESULT_RECEIVER_H
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IResultReceiver : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(ResultReceiver);
-
-    virtual void send(int32_t resultCode) = 0;
-
-    enum {
-        OP_SEND = IBinder::FIRST_CALL_TRANSACTION
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnResultReceiver : public BnInterface<IResultReceiver>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IRESULT_RECEIVER_H
-
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
deleted file mode 100644
index 7ccd9fe..0000000
--- a/include/binder/IServiceManager.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-#ifndef ANDROID_ISERVICE_MANAGER_H
-#define ANDROID_ISERVICE_MANAGER_H
-
-#include <binder/IInterface.h>
-#include <binder/IPermissionController.h>
-#include <utils/Vector.h>
-#include <utils/String16.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IServiceManager : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(ServiceManager);
-
-    /**
-     * Retrieve an existing service, blocking for a few seconds
-     * if it doesn't yet exist.
-     */
-    virtual sp<IBinder>         getService( const String16& name) const = 0;
-
-    /**
-     * Retrieve an existing service, non-blocking.
-     */
-    virtual sp<IBinder>         checkService( const String16& name) const = 0;
-
-    /**
-     * Register a service.
-     */
-    virtual status_t            addService( const String16& name,
-                                            const sp<IBinder>& service,
-                                            bool allowIsolated = false) = 0;
-
-    /**
-     * Return list of all existing services.
-     */
-    virtual Vector<String16>    listServices() = 0;
-
-    enum {
-        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        CHECK_SERVICE_TRANSACTION,
-        ADD_SERVICE_TRANSACTION,
-        LIST_SERVICES_TRANSACTION,
-    };
-};
-
-sp<IServiceManager> defaultServiceManager();
-
-template<typename INTERFACE>
-status_t getService(const String16& name, sp<INTERFACE>* outService)
-{
-    const sp<IServiceManager> sm = defaultServiceManager();
-    if (sm != NULL) {
-        *outService = interface_cast<INTERFACE>(sm->getService(name));
-        if ((*outService) != NULL) return NO_ERROR;
-    }
-    return NAME_NOT_FOUND;
-}
-
-bool checkCallingPermission(const String16& permission);
-bool checkCallingPermission(const String16& permission,
-                            int32_t* outPid, int32_t* outUid);
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
-
-}; // namespace android
-
-#endif // ANDROID_ISERVICE_MANAGER_H
-
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
deleted file mode 100644
index 2490b82..0000000
--- a/include/binder/Parcel.h
+++ /dev/null
@@ -1,798 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PARCEL_H
-#define ANDROID_PARCEL_H
-
-#include <string>
-#include <vector>
-
-#include <cutils/native_handle.h>
-#include <nativehelper/ScopedFd.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-#include <utils/Flattenable.h>
-#include <linux/binder.h>
-
-#include <binder/IInterface.h>
-#include <binder/Parcelable.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-template <typename T> class Flattenable;
-template <typename T> class LightFlattenable;
-class IBinder;
-class IPCThreadState;
-class ProcessState;
-class String8;
-class TextOutput;
-
-class Parcel {
-    friend class IPCThreadState;
-public:
-    class ReadableBlob;
-    class WritableBlob;
-
-                        Parcel();
-                        ~Parcel();
-    
-    const uint8_t*      data() const;
-    size_t              dataSize() const;
-    size_t              dataAvail() const;
-    size_t              dataPosition() const;
-    size_t              dataCapacity() const;
-
-    status_t            setDataSize(size_t size);
-    void                setDataPosition(size_t pos) const;
-    status_t            setDataCapacity(size_t size);
-    
-    status_t            setData(const uint8_t* buffer, size_t len);
-
-    status_t            appendFrom(const Parcel *parcel,
-                                   size_t start, size_t len);
-
-    bool                allowFds() const;
-    bool                pushAllowFds(bool allowFds);
-    void                restoreAllowFds(bool lastValue);
-
-    bool                hasFileDescriptors() const;
-
-    // Writes the RPC header.
-    status_t            writeInterfaceToken(const String16& interface);
-
-    // Parses the RPC header, returning true if the interface name
-    // in the header matches the expected interface from the caller.
-    //
-    // Additionally, enforceInterface does part of the work of
-    // propagating the StrictMode policy mask, populating the current
-    // IPCThreadState, which as an optimization may optionally be
-    // passed in.
-    bool                enforceInterface(const String16& interface,
-                                         IPCThreadState* threadState = NULL) const;
-    bool                checkInterface(IBinder*) const;
-
-    void                freeData();
-
-private:
-    const binder_size_t* objects() const;
-
-public:
-    size_t              objectsCount() const;
-    
-    status_t            errorCheck() const;
-    void                setError(status_t err);
-    
-    status_t            write(const void* data, size_t len);
-    void*               writeInplace(size_t len);
-    status_t            writeUnpadded(const void* data, size_t len);
-    status_t            writeInt32(int32_t val);
-    status_t            writeUint32(uint32_t val);
-    status_t            writeInt64(int64_t val);
-    status_t            writeUint64(uint64_t val);
-    status_t            writeFloat(float val);
-    status_t            writeDouble(double val);
-    status_t            writeCString(const char* str);
-    status_t            writeString8(const String8& str);
-    status_t            writeString16(const String16& str);
-    status_t            writeString16(const std::unique_ptr<String16>& str);
-    status_t            writeString16(const char16_t* str, size_t len);
-    status_t            writeStrongBinder(const sp<IBinder>& val);
-    status_t            writeWeakBinder(const wp<IBinder>& val);
-    status_t            writeInt32Array(size_t len, const int32_t *val);
-    status_t            writeByteArray(size_t len, const uint8_t *val);
-    status_t            writeBool(bool val);
-    status_t            writeChar(char16_t val);
-    status_t            writeByte(int8_t val);
-
-    // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
-    status_t            writeUtf8AsUtf16(const std::string& str);
-    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
-
-    status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
-    status_t            writeByteVector(const std::vector<int8_t>& val);
-    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
-    status_t            writeByteVector(const std::vector<uint8_t>& val);
-    status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
-    status_t            writeInt32Vector(const std::vector<int32_t>& val);
-    status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
-    status_t            writeInt64Vector(const std::vector<int64_t>& val);
-    status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
-    status_t            writeFloatVector(const std::vector<float>& val);
-    status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
-    status_t            writeDoubleVector(const std::vector<double>& val);
-    status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
-    status_t            writeBoolVector(const std::vector<bool>& val);
-    status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
-    status_t            writeCharVector(const std::vector<char16_t>& val);
-    status_t            writeString16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
-    status_t            writeString16Vector(const std::vector<String16>& val);
-    status_t            writeUtf8VectorAsUtf16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
-    status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
-
-    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
-    status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
-
-    template<typename T>
-    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
-    template<typename T>
-    status_t            writeParcelableVector(const std::vector<T>& val);
-
-    template<typename T>
-    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable);
-
-    status_t            writeParcelable(const Parcelable& parcelable);
-
-    template<typename T>
-    status_t            write(const Flattenable<T>& val);
-
-    template<typename T>
-    status_t            write(const LightFlattenable<T>& val);
-
-
-    // Place a native_handle into the parcel (the native_handle's file-
-    // descriptors are dup'ed, so it is safe to delete the native_handle
-    // when this function returns).
-    // Doesn't take ownership of the native_handle.
-    status_t            writeNativeHandle(const native_handle* handle);
-    
-    // Place a file descriptor into the parcel.  The given fd must remain
-    // valid for the lifetime of the parcel.
-    // The Parcel does not take ownership of the given fd unless you ask it to.
-    status_t            writeFileDescriptor(int fd, bool takeOwnership = false);
-    
-    // Place a file descriptor into the parcel.  A dup of the fd is made, which
-    // will be closed once the parcel is destroyed.
-    status_t            writeDupFileDescriptor(int fd);
-
-    // Place a file descriptor into the parcel.  This will not affect the
-    // semantics of the smart file descriptor. A new descriptor will be
-    // created, and will be closed when the parcel is destroyed.
-    status_t            writeUniqueFileDescriptor(
-                            const ScopedFd& fd);
-
-    // Place a vector of file desciptors into the parcel. Each descriptor is
-    // dup'd as in writeDupFileDescriptor
-    status_t            writeUniqueFileDescriptorVector(
-                            const std::unique_ptr<std::vector<ScopedFd>>& val);
-    status_t            writeUniqueFileDescriptorVector(
-                            const std::vector<ScopedFd>& val);
-
-    // Writes a blob to the parcel.
-    // If the blob is small, then it is stored in-place, otherwise it is
-    // transferred by way of an anonymous shared memory region.  Prefer sending
-    // immutable blobs if possible since they may be subsequently transferred between
-    // processes without further copying whereas mutable blobs always need to be copied.
-    // The caller should call release() on the blob after writing its contents.
-    status_t            writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
-
-    // Write an existing immutable blob file descriptor to the parcel.
-    // This allows the client to send the same blob to multiple processes
-    // as long as it keeps a dup of the blob file descriptor handy for later.
-    status_t            writeDupImmutableBlobFileDescriptor(int fd);
-
-    status_t            writeObject(const flat_binder_object& val, bool nullMetaData);
-
-    // Like Parcel.java's writeNoException().  Just writes a zero int32.
-    // Currently the native implementation doesn't do any of the StrictMode
-    // stack gathering and serialization that the Java implementation does.
-    status_t            writeNoException();
-
-    void                remove(size_t start, size_t amt);
-    
-    status_t            read(void* outData, size_t len) const;
-    const void*         readInplace(size_t len) const;
-    int32_t             readInt32() const;
-    status_t            readInt32(int32_t *pArg) const;
-    uint32_t            readUint32() const;
-    status_t            readUint32(uint32_t *pArg) const;
-    int64_t             readInt64() const;
-    status_t            readInt64(int64_t *pArg) const;
-    uint64_t            readUint64() const;
-    status_t            readUint64(uint64_t *pArg) const;
-    float               readFloat() const;
-    status_t            readFloat(float *pArg) const;
-    double              readDouble() const;
-    status_t            readDouble(double *pArg) const;
-    intptr_t            readIntPtr() const;
-    status_t            readIntPtr(intptr_t *pArg) const;
-    bool                readBool() const;
-    status_t            readBool(bool *pArg) const;
-    char16_t            readChar() const;
-    status_t            readChar(char16_t *pArg) const;
-    int8_t              readByte() const;
-    status_t            readByte(int8_t *pArg) const;
-
-    // Read a UTF16 encoded string, convert to UTF8
-    status_t            readUtf8FromUtf16(std::string* str) const;
-    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
-
-    const char*         readCString() const;
-    String8             readString8() const;
-    String16            readString16() const;
-    status_t            readString16(String16* pArg) const;
-    status_t            readString16(std::unique_ptr<String16>* pArg) const;
-    const char16_t*     readString16Inplace(size_t* outLen) const;
-    sp<IBinder>         readStrongBinder() const;
-    status_t            readStrongBinder(sp<IBinder>* val) const;
-    wp<IBinder>         readWeakBinder() const;
-
-    template<typename T>
-    status_t            readParcelableVector(
-                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
-    template<typename T>
-    status_t            readParcelableVector(std::vector<T>* val) const;
-
-    status_t            readParcelable(Parcelable* parcelable) const;
-
-    template<typename T>
-    status_t            readParcelable(std::unique_ptr<T>* parcelable) const;
-
-    template<typename T>
-    status_t            readStrongBinder(sp<T>* val) const;
-
-    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
-    status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
-
-    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
-    status_t            readByteVector(std::vector<int8_t>* val) const;
-    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
-    status_t            readByteVector(std::vector<uint8_t>* val) const;
-    status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
-    status_t            readInt32Vector(std::vector<int32_t>* val) const;
-    status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
-    status_t            readInt64Vector(std::vector<int64_t>* val) const;
-    status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
-    status_t            readFloatVector(std::vector<float>* val) const;
-    status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
-    status_t            readDoubleVector(std::vector<double>* val) const;
-    status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
-    status_t            readBoolVector(std::vector<bool>* val) const;
-    status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
-    status_t            readCharVector(std::vector<char16_t>* val) const;
-    status_t            readString16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
-    status_t            readString16Vector(std::vector<String16>* val) const;
-    status_t            readUtf8VectorFromUtf16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
-    status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
-
-    template<typename T>
-    status_t            read(Flattenable<T>& val) const;
-
-    template<typename T>
-    status_t            read(LightFlattenable<T>& val) const;
-
-    // Like Parcel.java's readExceptionCode().  Reads the first int32
-    // off of a Parcel's header, returning 0 or the negative error
-    // code on exceptions, but also deals with skipping over rich
-    // response headers.  Callers should use this to read & parse the
-    // response headers rather than doing it by hand.
-    int32_t             readExceptionCode() const;
-
-    // Retrieve native_handle from the parcel. This returns a copy of the
-    // parcel's native_handle (the caller takes ownership). The caller
-    // must free the native_handle with native_handle_close() and 
-    // native_handle_delete().
-    native_handle*     readNativeHandle() const;
-
-    
-    // Retrieve a file descriptor from the parcel.  This returns the raw fd
-    // in the parcel, which you do not own -- use dup() to get your own copy.
-    int                 readFileDescriptor() const;
-
-    // Retrieve a smart file descriptor from the parcel.
-    status_t            readUniqueFileDescriptor(
-                            ScopedFd* val) const;
-
-
-    // Retrieve a vector of smart file descriptors from the parcel.
-    status_t            readUniqueFileDescriptorVector(
-                            std::unique_ptr<std::vector<ScopedFd>>* val) const;
-    status_t            readUniqueFileDescriptorVector(
-                            std::vector<ScopedFd>* val) const;
-
-    // Reads a blob from the parcel.
-    // The caller should call release() on the blob after reading its contents.
-    status_t            readBlob(size_t len, ReadableBlob* outBlob) const;
-
-    const flat_binder_object* readObject(bool nullMetaData) const;
-
-    // Explicitly close all file descriptors in the parcel.
-    void                closeFileDescriptors();
-
-    // Debugging: get metrics on current allocations.
-    static size_t       getGlobalAllocSize();
-    static size_t       getGlobalAllocCount();
-
-private:
-    typedef void        (*release_func)(Parcel* parcel,
-                                        const uint8_t* data, size_t dataSize,
-                                        const binder_size_t* objects, size_t objectsSize,
-                                        void* cookie);
-                        
-    uintptr_t           ipcData() const;
-    size_t              ipcDataSize() const;
-    uintptr_t           ipcObjects() const;
-    size_t              ipcObjectsCount() const;
-    void                ipcSetDataReference(const uint8_t* data, size_t dataSize,
-                                            const binder_size_t* objects, size_t objectsCount,
-                                            release_func relFunc, void* relCookie);
-    
-public:
-    void                print(TextOutput& to, uint32_t flags = 0) const;
-
-private:
-                        Parcel(const Parcel& o);
-    Parcel&             operator=(const Parcel& o);
-    
-    status_t            finishWrite(size_t len);
-    void                releaseObjects();
-    void                acquireObjects();
-    status_t            growData(size_t len);
-    status_t            restartWrite(size_t desired);
-    status_t            continueWrite(size_t desired);
-    status_t            writePointer(uintptr_t val);
-    status_t            readPointer(uintptr_t *pArg) const;
-    uintptr_t           readPointer() const;
-    void                freeDataNoInit();
-    void                initState();
-    void                scanForFds() const;
-                        
-    template<class T>
-    status_t            readAligned(T *pArg) const;
-
-    template<class T>   T readAligned() const;
-
-    template<class T>
-    status_t            writeAligned(T val);
-
-    status_t            writeRawNullableParcelable(const Parcelable*
-                                                   parcelable);
-
-    template<typename T, typename U>
-    status_t            unsafeReadTypedVector(std::vector<T>* val,
-                                              status_t(Parcel::*read_func)(U*) const) const;
-    template<typename T>
-    status_t            readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
-                                                status_t(Parcel::*read_func)(T*) const) const;
-    template<typename T>
-    status_t            readTypedVector(std::vector<T>* val,
-                                        status_t(Parcel::*read_func)(T*) const) const;
-    template<typename T, typename U>
-    status_t            unsafeWriteTypedVector(const std::vector<T>& val,
-                                               status_t(Parcel::*write_func)(U));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(const T&));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(T));
-    template<typename T>
-    status_t            writeTypedVector(const std::vector<T>& val,
-                                         status_t(Parcel::*write_func)(const T&));
-    template<typename T>
-    status_t            writeTypedVector(const std::vector<T>& val,
-                                         status_t(Parcel::*write_func)(T));
-
-    status_t            mError;
-    uint8_t*            mData;
-    size_t              mDataSize;
-    size_t              mDataCapacity;
-    mutable size_t      mDataPos;
-    binder_size_t*      mObjects;
-    size_t              mObjectsSize;
-    size_t              mObjectsCapacity;
-    mutable size_t      mNextObjectHint;
-
-    mutable bool        mFdsKnown;
-    mutable bool        mHasFds;
-    bool                mAllowFds;
-
-    release_func        mOwner;
-    void*               mOwnerCookie;
-
-    class Blob {
-    public:
-        Blob();
-        ~Blob();
-
-        void clear();
-        void release();
-        inline size_t size() const { return mSize; }
-        inline int fd() const { return mFd; };
-        inline bool isMutable() const { return mMutable; }
-
-    protected:
-        void init(int fd, void* data, size_t size, bool isMutable);
-
-        int mFd; // owned by parcel so not closed when released
-        void* mData;
-        size_t mSize;
-        bool mMutable;
-    };
-
-    class FlattenableHelperInterface {
-    protected:
-        ~FlattenableHelperInterface() { }
-    public:
-        virtual size_t getFlattenedSize() const = 0;
-        virtual size_t getFdCount() const = 0;
-        virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0;
-        virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0;
-    };
-
-    template<typename T>
-    class FlattenableHelper : public FlattenableHelperInterface {
-        friend class Parcel;
-        const Flattenable<T>& val;
-        explicit FlattenableHelper(const Flattenable<T>& val) : val(val) { }
-
-    public:
-        virtual size_t getFlattenedSize() const {
-            return val.getFlattenedSize();
-        }
-        virtual size_t getFdCount() const {
-            return val.getFdCount();
-        }
-        virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const {
-            return val.flatten(buffer, size, fds, count);
-        }
-        virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) {
-            return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count);
-        }
-    };
-    status_t write(const FlattenableHelperInterface& val);
-    status_t read(FlattenableHelperInterface& val) const;
-
-public:
-    class ReadableBlob : public Blob {
-        friend class Parcel;
-    public:
-        inline const void* data() const { return mData; }
-        inline void* mutableData() { return isMutable() ? mData : NULL; }
-    };
-
-    class WritableBlob : public Blob {
-        friend class Parcel;
-    public:
-        inline void* data() { return mData; }
-    };
-
-private:
-    size_t mOpenAshmemSize;
-
-public:
-    // TODO: Remove once ABI can be changed.
-    size_t getBlobAshmemSize() const;
-    size_t getOpenAshmemSize() const;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename T>
-status_t Parcel::write(const Flattenable<T>& val) {
-    const FlattenableHelper<T> helper(val);
-    return write(helper);
-}
-
-template<typename T>
-status_t Parcel::write(const LightFlattenable<T>& val) {
-    size_t size(val.getFlattenedSize());
-    if (!val.isFixedSize()) {
-        status_t err = writeInt32(size);
-        if (err != NO_ERROR) {
-            return err;
-        }
-    }
-    if (size) {
-        void* buffer = writeInplace(size);
-        if (buffer == NULL)
-            return NO_MEMORY;
-        return val.flatten(buffer, size);
-    }
-    return NO_ERROR;
-}
-
-template<typename T>
-status_t Parcel::read(Flattenable<T>& val) const {
-    FlattenableHelper<T> helper(val);
-    return read(helper);
-}
-
-template<typename T>
-status_t Parcel::read(LightFlattenable<T>& val) const {
-    size_t size;
-    if (val.isFixedSize()) {
-        size = val.getFlattenedSize();
-    } else {
-        int32_t s;
-        status_t err = readInt32(&s);
-        if (err != NO_ERROR) {
-            return err;
-        }
-        size = s;
-    }
-    if (size) {
-        void const* buffer = readInplace(size);
-        return buffer == NULL ? NO_MEMORY :
-                val.unflatten(buffer, size);
-    }
-    return NO_ERROR;
-}
-
-template<typename T>
-status_t Parcel::readStrongBinder(sp<T>* val) const {
-    sp<IBinder> tmp;
-    status_t ret = readStrongBinder(&tmp);
-
-    if (ret == OK) {
-        *val = interface_cast<T>(tmp);
-
-        if (val->get() == nullptr) {
-            return UNKNOWN_ERROR;
-        }
-    }
-
-    return ret;
-}
-
-template<typename T, typename U>
-status_t Parcel::unsafeReadTypedVector(
-        std::vector<T>* val,
-        status_t(Parcel::*read_func)(U*) const) const {
-    int32_t size;
-    status_t status = this->readInt32(&size);
-
-    if (status != OK) {
-        return status;
-    }
-
-    if (size < 0) {
-        return UNEXPECTED_NULL;
-    }
-
-    if (val->max_size() < size) {
-        return NO_MEMORY;
-    }
-
-    val->resize(size);
-
-    if (val->size() < size) {
-        return NO_MEMORY;
-    }
-
-    for (auto& v: *val) {
-        status = (this->*read_func)(&v);
-
-        if (status != OK) {
-            return status;
-        }
-    }
-
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::readTypedVector(std::vector<T>* val,
-                                 status_t(Parcel::*read_func)(T*) const) const {
-    return unsafeReadTypedVector(val, read_func);
-}
-
-template<typename T>
-status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
-                                         status_t(Parcel::*read_func)(T*) const) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->reset(new std::vector<T>());
-
-    status = unsafeReadTypedVector(val->get(), read_func);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-template<typename T, typename U>
-status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val,
-                                        status_t(Parcel::*write_func)(U)) {
-    if (val.size() > std::numeric_limits<int32_t>::max()) {
-        return BAD_VALUE;
-    }
-
-    status_t status = this->writeInt32(val.size());
-
-    if (status != OK) {
-        return status;
-    }
-
-    for (const auto& item : val) {
-        status = (this->*write_func)(item);
-
-        if (status != OK) {
-            return status;
-        }
-    }
-
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
-                                  status_t(Parcel::*write_func)(const T&)) {
-    return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
-                                  status_t(Parcel::*write_func)(T)) {
-    return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(const T&)) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(T)) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::vector<T>* val) const {
-    return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->reset(new std::vector<std::unique_ptr<T>>());
-
-    status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
-    const int32_t start = dataPosition();
-    int32_t present;
-    status_t status = readInt32(&present);
-    parcelable->reset();
-
-    if (status != OK || !present) {
-        return status;
-    }
-
-    setDataPosition(start);
-    parcelable->reset(new T());
-
-    status = readParcelable(parcelable->get());
-
-    if (status != OK) {
-        parcelable->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
-    return writeRawNullableParcelable(parcelable.get());
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::vector<T>& val) {
-    return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, &Parcel::writeParcelable);
-}
-
-// ---------------------------------------------------------------------------
-
-inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
-{
-    parcel.print(to);
-    return to;
-}
-
-// ---------------------------------------------------------------------------
-
-// Generic acquire and release of objects.
-void acquire_object(const sp<ProcessState>& proc,
-                    const flat_binder_object& obj, const void* who);
-void release_object(const sp<ProcessState>& proc,
-                    const flat_binder_object& obj, const void* who);
-
-void flatten_binder(const sp<ProcessState>& proc,
-                    const sp<IBinder>& binder, flat_binder_object* out);
-void flatten_binder(const sp<ProcessState>& proc,
-                    const wp<IBinder>& binder, flat_binder_object* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
-                          const flat_binder_object& flat, sp<IBinder>* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
-                          const flat_binder_object& flat, wp<IBinder>* out);
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PARCEL_H
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
deleted file mode 100644
index faf0d34..0000000
--- a/include/binder/Parcelable.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PARCELABLE_H
-#define ANDROID_PARCELABLE_H
-
-#include <vector>
-
-#include <utils/Errors.h>
-#include <utils/String16.h>
-
-namespace android {
-
-class Parcel;
-
-// Abstract interface of all parcelables.
-class Parcelable {
-public:
-    virtual ~Parcelable() = default;
-
-    // Write |this| parcelable to the given |parcel|.  Keep in mind that
-    // implementations of writeToParcel must be manually kept in sync
-    // with readFromParcel and the Java equivalent versions of these methods.
-    //
-    // Returns android::OK on success and an appropriate error otherwise.
-    virtual status_t writeToParcel(Parcel* parcel) const = 0;
-
-    // Read data from the given |parcel| into |this|.  After readFromParcel
-    // completes, |this| should have equivalent state to the object that
-    // wrote itself to the parcel.
-    //
-    // Returns android::OK on success and an appropriate error otherwise.
-    virtual status_t readFromParcel(const Parcel* parcel) = 0;
-};  // class Parcelable
-
-}  // namespace android
-
-#endif // ANDROID_PARCELABLE_H
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
deleted file mode 100644
index fe5619f..0000000
--- a/include/binder/PersistableBundle.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PERSISTABLE_BUNDLE_H
-#define ANDROID_PERSISTABLE_BUNDLE_H
-
-#include <map>
-#include <vector>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace os {
-
-/*
- * C++ implementation of PersistableBundle, a mapping from String values to
- * various types that can be saved to persistent and later restored.
- */
-class PersistableBundle : public Parcelable {
-public:
-    PersistableBundle() = default;
-    virtual ~PersistableBundle() = default;
-    PersistableBundle(const PersistableBundle& bundle) = default;
-
-    status_t writeToParcel(Parcel* parcel) const override;
-    status_t readFromParcel(const Parcel* parcel) override;
-
-    bool empty() const;
-    size_t size() const;
-    size_t erase(const String16& key);
-
-    /*
-     * Setters for PersistableBundle. Adds a a key-value pair instantiated with
-     * |key| and |value| into the member map appropriate for the type of |value|.
-     * If there is already an existing value for |key|, |value| will replace it.
-     */
-    void putBoolean(const String16& key, bool value);
-    void putInt(const String16& key, int32_t value);
-    void putLong(const String16& key, int64_t value);
-    void putDouble(const String16& key, double value);
-    void putString(const String16& key, const String16& value);
-    void putBooleanVector(const String16& key, const std::vector<bool>& value);
-    void putIntVector(const String16& key, const std::vector<int32_t>& value);
-    void putLongVector(const String16& key, const std::vector<int64_t>& value);
-    void putDoubleVector(const String16& key, const std::vector<double>& value);
-    void putStringVector(const String16& key, const std::vector<String16>& value);
-    void putPersistableBundle(const String16& key, const PersistableBundle& value);
-
-    /*
-     * Getters for PersistableBundle. If |key| exists, these methods write the
-     * value associated with |key| into |out|, and return true. Otherwise, these
-     * methods return false.
-     */
-    bool getBoolean(const String16& key, bool* out) const;
-    bool getInt(const String16& key, int32_t* out) const;
-    bool getLong(const String16& key, int64_t* out) const;
-    bool getDouble(const String16& key, double* out) const;
-    bool getString(const String16& key, String16* out) const;
-    bool getBooleanVector(const String16& key, std::vector<bool>* out) const;
-    bool getIntVector(const String16& key, std::vector<int32_t>* out) const;
-    bool getLongVector(const String16& key, std::vector<int64_t>* out) const;
-    bool getDoubleVector(const String16& key, std::vector<double>* out) const;
-    bool getStringVector(const String16& key, std::vector<String16>* out) const;
-    bool getPersistableBundle(const String16& key, PersistableBundle* out) const;
-
-    friend bool operator==(const PersistableBundle& lhs, const PersistableBundle& rhs) {
-        return (lhs.mBoolMap == rhs.mBoolMap && lhs.mIntMap == rhs.mIntMap &&
-                lhs.mLongMap == rhs.mLongMap && lhs.mDoubleMap == rhs.mDoubleMap &&
-                lhs.mStringMap == rhs.mStringMap && lhs.mBoolVectorMap == rhs.mBoolVectorMap &&
-                lhs.mIntVectorMap == rhs.mIntVectorMap &&
-                lhs.mLongVectorMap == rhs.mLongVectorMap &&
-                lhs.mDoubleVectorMap == rhs.mDoubleVectorMap &&
-                lhs.mStringVectorMap == rhs.mStringVectorMap &&
-                lhs.mPersistableBundleMap == rhs.mPersistableBundleMap);
-    }
-
-    friend bool operator!=(const PersistableBundle& lhs, const PersistableBundle& rhs) {
-        return !(lhs == rhs);
-    }
-
-private:
-    status_t writeToParcelInner(Parcel* parcel) const;
-    status_t readFromParcelInner(const Parcel* parcel, size_t length);
-
-    std::map<String16, bool> mBoolMap;
-    std::map<String16, int32_t> mIntMap;
-    std::map<String16, int64_t> mLongMap;
-    std::map<String16, double> mDoubleMap;
-    std::map<String16, String16> mStringMap;
-    std::map<String16, std::vector<bool>> mBoolVectorMap;
-    std::map<String16, std::vector<int32_t>> mIntVectorMap;
-    std::map<String16, std::vector<int64_t>> mLongVectorMap;
-    std::map<String16, std::vector<double>> mDoubleVectorMap;
-    std::map<String16, std::vector<String16>> mStringVectorMap;
-    std::map<String16, PersistableBundle> mPersistableBundleMap;
-};
-
-}  // namespace os
-
-}  // namespace android
-
-#endif  // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/include/binder/ProcessInfoService.h b/include/binder/ProcessInfoService.h
deleted file mode 100644
index c5ead20..0000000
--- a/include/binder/ProcessInfoService.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PROCESS_INFO_SERVICE_H
-#define ANDROID_PROCESS_INFO_SERVICE_H
-
-#include <binder/IProcessInfoService.h>
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-#include <sys/types.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class ProcessInfoService : public Singleton<ProcessInfoService> {
-
-    friend class Singleton<ProcessInfoService>;
-    sp<IProcessInfoService> mProcessInfoService;
-    Mutex mProcessInfoLock;
-
-    ProcessInfoService();
-
-    status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
-    void updateBinderLocked();
-
-    static const int BINDER_ATTEMPT_LIMIT = 5;
-
-public:
-
-    /**
-     * For each PID in the given "pids" input array, write the current process state
-     * for that process into the "states" output array, or
-     * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
-     * exists.
-     *
-     * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
-     */
-    static status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
-            /*out*/ int32_t* states) {
-        return ProcessInfoService::getInstance().getProcessStatesImpl(length, /*in*/ pids,
-                /*out*/ states);
-    }
-
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_PROCESS_INFO_SERVICE_H
-
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
deleted file mode 100644
index 64cf72e..0000000
--- a/include/binder/ProcessState.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_PROCESS_STATE_H
-#define ANDROID_PROCESS_STATE_H
-
-#include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-#include <utils/threads.h>
-
-#include <pthread.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IPCThreadState;
-
-class ProcessState : public virtual RefBase
-{
-public:
-    static  sp<ProcessState>    self();
-
-            void                setContextObject(const sp<IBinder>& object);
-            sp<IBinder>         getContextObject(const sp<IBinder>& caller);
-        
-            void                setContextObject(const sp<IBinder>& object,
-                                                 const String16& name);
-            sp<IBinder>         getContextObject(const String16& name,
-                                                 const sp<IBinder>& caller);
-
-            void                startThreadPool();
-                        
-    typedef bool (*context_check_func)(const String16& name,
-                                       const sp<IBinder>& caller,
-                                       void* userData);
-        
-            bool                isContextManager(void) const;
-            bool                becomeContextManager(
-                                    context_check_func checkFunc,
-                                    void* userData);
-
-            sp<IBinder>         getStrongProxyForHandle(int32_t handle);
-            wp<IBinder>         getWeakProxyForHandle(int32_t handle);
-            void                expungeHandle(int32_t handle, IBinder* binder);
-
-            void                spawnPooledThread(bool isMain);
-            
-            status_t            setThreadPoolMaxThreadCount(size_t maxThreads);
-            void                giveThreadPoolName();
-
-private:
-    friend class IPCThreadState;
-    
-                                ProcessState();
-                                ~ProcessState();
-
-                                ProcessState(const ProcessState& o);
-            ProcessState&       operator=(const ProcessState& o);
-            String8             makeBinderThreadName();
-
-            struct handle_entry {
-                IBinder* binder;
-                RefBase::weakref_type* refs;
-            };
-
-            handle_entry*       lookupHandleLocked(int32_t handle);
-
-            int                 mDriverFD;
-            void*               mVMStart;
-
-            // Protects thread count variable below.
-            pthread_mutex_t     mThreadCountLock;
-            pthread_cond_t      mThreadCountDecrement;
-            // Number of binder threads current executing a command.
-            size_t              mExecutingThreadsCount;
-            // Maximum number for binder threads allowed for this process.
-            size_t              mMaxThreads;
-            // Time when thread pool was emptied
-            int64_t             mStarvationStartTimeMs;
-
-    mutable Mutex               mLock;  // protects everything below.
-
-            Vector<handle_entry>mHandleToObject;
-
-            bool                mManagesContexts;
-            context_check_func  mBinderContextCheckFunc;
-            void*               mBinderContextUserData;
-
-            KeyedVector<String16, sp<IBinder> >
-                                mContexts;
-
-
-            String8             mRootDir;
-            bool                mThreadPoolStarted;
-    volatile int32_t            mThreadPoolSeq;
-};
-    
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PROCESS_STATE_H
diff --git a/include/binder/Status.h b/include/binder/Status.h
deleted file mode 100644
index ce947fa..0000000
--- a/include/binder/Status.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_BINDER_STATUS_H
-#define ANDROID_BINDER_STATUS_H
-
-#include <cstdint>
-
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace binder {
-
-// An object similar in function to a status_t except that it understands
-// how exceptions are encoded in the prefix of a Parcel. Used like:
-//
-//     Parcel data;
-//     Parcel reply;
-//     status_t status;
-//     binder::Status remote_exception;
-//     if ((status = data.writeInterfaceToken(interface_descriptor)) != OK ||
-//         (status = data.writeInt32(function_input)) != OK) {
-//         // We failed to write into the memory of our local parcel?
-//     }
-//     if ((status = remote()->transact(transaction, data, &reply)) != OK) {
-//        // Something has gone wrong in the binder driver or libbinder.
-//     }
-//     if ((status = remote_exception.readFromParcel(reply)) != OK) {
-//         // The remote didn't correctly write the exception header to the
-//         // reply.
-//     }
-//     if (!remote_exception.isOk()) {
-//         // The transaction went through correctly, but the remote reported an
-//         // exception during handling.
-//     }
-//
-class Status final {
-public:
-    // Keep the exception codes in sync with android/os/Parcel.java.
-    enum Exception {
-        EX_NONE = 0,
-        EX_SECURITY = -1,
-        EX_BAD_PARCELABLE = -2,
-        EX_ILLEGAL_ARGUMENT = -3,
-        EX_NULL_POINTER = -4,
-        EX_ILLEGAL_STATE = -5,
-        EX_NETWORK_MAIN_THREAD = -6,
-        EX_UNSUPPORTED_OPERATION = -7,
-        EX_SERVICE_SPECIFIC = -8,
-
-        // This is special and Java specific; see Parcel.java.
-        EX_HAS_REPLY_HEADER = -128,
-        // This is special, and indicates to C++ binder proxies that the
-        // transaction has failed at a low level.
-        EX_TRANSACTION_FAILED = -129,
-    };
-
-    // A more readable alias for the default constructor.
-    static Status ok();
-    // Authors should explicitly pick whether their integer is:
-    //  - an exception code (EX_* above)
-    //  - service specific error code
-    //  - status_t
-    //
-    //  Prefer a generic exception code when possible, then a service specific
-    //  code, and finally a status_t for low level failures or legacy support.
-    //  Exception codes and service specific errors map to nicer exceptions for
-    //  Java clients.
-    static Status fromExceptionCode(int32_t exceptionCode);
-    static Status fromExceptionCode(int32_t exceptionCode,
-                                    const String8& message);
-    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
-    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
-                                           const String8& message);
-    static Status fromStatusT(status_t status);
-
-    Status() = default;
-    ~Status() = default;
-
-    // Status objects are copyable and contain just simple data.
-    Status(const Status& status) = default;
-    Status(Status&& status) = default;
-    Status& operator=(const Status& status) = default;
-
-    // Bear in mind that if the client or service is a Java endpoint, this
-    // is not the logic which will provide/interpret the data here.
-    status_t readFromParcel(const Parcel& parcel);
-    status_t writeToParcel(Parcel* parcel) const;
-
-    // Set one of the pre-defined exception types defined above.
-    void setException(int32_t ex, const String8& message);
-    // Set a service specific exception with error code.
-    void setServiceSpecificError(int32_t errorCode, const String8& message);
-    // Setting a |status| != OK causes generated code to return |status|
-    // from Binder transactions, rather than writing an exception into the
-    // reply Parcel.  This is the least preferable way of reporting errors.
-    void setFromStatusT(status_t status);
-
-    // Get information about an exception.
-    int32_t exceptionCode() const  { return mException; }
-    const String8& exceptionMessage() const { return mMessage; }
-    status_t transactionError() const {
-        return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK;
-    }
-    int32_t serviceSpecificErrorCode() const {
-        return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0;
-    }
-
-    bool isOk() const { return mException == EX_NONE; }
-
-    // For logging.
-    String8 toString8() const;
-
-private:
-    Status(int32_t exceptionCode, int32_t errorCode);
-    Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
-
-    // If |mException| == EX_TRANSACTION_FAILED, generated code will return
-    // |mErrorCode| as the result of the transaction rather than write an
-    // exception to the reply parcel.
-    //
-    // Otherwise, we always write |mException| to the parcel.
-    // If |mException| !=  EX_NONE, we write |mMessage| as well.
-    // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well.
-    int32_t mException = EX_NONE;
-    int32_t mErrorCode = 0;
-    String8 mMessage;
-};  // class Status
-
-// For gtest output logging
-template<typename T>
-T& operator<< (T& stream, const Status& s) {
-    stream << s.toString8().string();
-    return stream;
-}
-
-}  // namespace binder
-}  // namespace android
-
-#endif // ANDROID_BINDER_STATUS_H
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
deleted file mode 100644
index 974a194..0000000
--- a/include/binder/TextOutput.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TEXTOUTPUT_H
-#define ANDROID_TEXTOUTPUT_H
-
-#include <utils/Errors.h>
-
-#include <stdint.h>
-#include <string.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class String8;
-class String16;
-
-class TextOutput
-{
-public:
-                        TextOutput();
-    virtual             ~TextOutput();
-    
-    virtual status_t    print(const char* txt, size_t len) = 0;
-    virtual void        moveIndent(int delta) = 0;
-    
-    class Bundle {
-    public:
-        inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); }
-        inline ~Bundle() { mTO.popBundle(); }
-    private:
-        TextOutput&     mTO;
-    };
-    
-    virtual void        pushBundle() = 0;
-    virtual void        popBundle() = 0;
-};
-
-// ---------------------------------------------------------------------------
-
-// Text output stream for printing to the log (via utils/Log.h).
-extern TextOutput& alog;
-
-// Text output stream for printing to stdout.
-extern TextOutput& aout;
-
-// Text output stream for printing to stderr.
-extern TextOutput& aerr;
-
-typedef TextOutput& (*TextOutputManipFunc)(TextOutput&);
-
-TextOutput& endl(TextOutput& to);
-TextOutput& indent(TextOutput& to);
-TextOutput& dedent(TextOutput& to);
-
-TextOutput& operator<<(TextOutput& to, const char* str);
-TextOutput& operator<<(TextOutput& to, char);     // writes raw character
-TextOutput& operator<<(TextOutput& to, bool);
-TextOutput& operator<<(TextOutput& to, int);
-TextOutput& operator<<(TextOutput& to, long);
-TextOutput& operator<<(TextOutput& to, unsigned int);
-TextOutput& operator<<(TextOutput& to, unsigned long);
-TextOutput& operator<<(TextOutput& to, long long);
-TextOutput& operator<<(TextOutput& to, unsigned long long);
-TextOutput& operator<<(TextOutput& to, float);
-TextOutput& operator<<(TextOutput& to, double);
-TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
-TextOutput& operator<<(TextOutput& to, const void*);
-TextOutput& operator<<(TextOutput& to, const String8& val);
-TextOutput& operator<<(TextOutput& to, const String16& val);
-
-class TypeCode 
-{
-public:
-    inline TypeCode(uint32_t code);
-    inline ~TypeCode();
-
-    inline uint32_t typeCode() const;
-    
-private:
-    uint32_t mCode;
-};
-
-TextOutput& operator<<(TextOutput& to, const TypeCode& val);
-
-class HexDump
-{
-public:
-    HexDump(const void *buf, size_t size, size_t bytesPerLine=16);
-    inline ~HexDump();
-    
-    inline HexDump& setBytesPerLine(size_t bytesPerLine);
-    inline HexDump& setSingleLineCutoff(int32_t bytes);
-    inline HexDump& setAlignment(size_t alignment);
-    inline HexDump& setCArrayStyle(bool enabled);
-    
-    inline const void* buffer() const;
-    inline size_t size() const;
-    inline size_t bytesPerLine() const;
-    inline int32_t singleLineCutoff() const;
-    inline size_t alignment() const;
-    inline bool carrayStyle() const;
-
-private:
-    const void* mBuffer;
-    size_t mSize;
-    size_t mBytesPerLine;
-    int32_t mSingleLineCutoff;
-    size_t mAlignment;
-    bool mCArrayStyle;
-};
-
-TextOutput& operator<<(TextOutput& to, const HexDump& val);
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline TextOutput& endl(TextOutput& to)
-{
-    to.print("\n", 1);
-    return to;
-}
-
-inline TextOutput& indent(TextOutput& to)
-{
-    to.moveIndent(1);
-    return to;
-}
-
-inline TextOutput& dedent(TextOutput& to)
-{
-    to.moveIndent(-1);
-    return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, const char* str)
-{
-    to.print(str, strlen(str));
-    return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, char c)
-{
-    to.print(&c, 1);
-    return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
-{
-    return (*func)(to);
-}
-
-inline TypeCode::TypeCode(uint32_t code) : mCode(code) { }
-inline TypeCode::~TypeCode() { }
-inline uint32_t TypeCode::typeCode() const { return mCode; }
-
-inline HexDump::~HexDump() { }
-
-inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) {
-    mBytesPerLine = bytesPerLine; return *this;
-}
-inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) {
-    mSingleLineCutoff = bytes; return *this;
-}
-inline HexDump& HexDump::setAlignment(size_t alignment) {
-    mAlignment = alignment; return *this;
-}
-inline HexDump& HexDump::setCArrayStyle(bool enabled) {
-    mCArrayStyle = enabled; return *this;
-}
-
-inline const void* HexDump::buffer() const { return mBuffer; }
-inline size_t HexDump::size() const { return mSize; }
-inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; }
-inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; }
-inline size_t HexDump::alignment() const { return mAlignment; }
-inline bool HexDump::carrayStyle() const { return mCArrayStyle; }
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_TEXTOUTPUT_H
diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format
new file mode 100644
index 0000000..86763a0
--- /dev/null
+++ b/include/gfx/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+BinPackParameters: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 8
+IndentWidth: 4
diff --git a/include/gui/BitTube.h b/include/gui/BitTube.h
deleted file mode 100644
index 3ecac52..0000000
--- a/include/gui/BitTube.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_CHANNEL_H
-#define ANDROID_GUI_SENSOR_CHANNEL_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <cutils/log.h>
-
-
-namespace android {
-// ----------------------------------------------------------------------------
-class Parcel;
-
-class BitTube : public RefBase
-{
-public:
-
-    // creates a BitTube with a default (4KB) send buffer
-    BitTube();
-
-    // creates a BitTube with a a specified send and receive buffer size
-    explicit BitTube(size_t bufsize);
-
-    explicit BitTube(const Parcel& data);
-    virtual ~BitTube();
-
-    // check state after construction
-    status_t initCheck() const;
-
-    // get receive file-descriptor
-    int getFd() const;
-
-    // get the send file-descriptor.
-    int getSendFd() const;
-
-    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
-    template <typename T>
-    static ssize_t sendObjects(const sp<BitTube>& tube,
-            T const* events, size_t count) {
-        return sendObjects(tube, events, count, sizeof(T));
-    }
-
-    // receive objects (sized blobs). If the receiving buffer isn't large enough,
-    // excess messages are silently discarded.
-    template <typename T>
-    static ssize_t recvObjects(const sp<BitTube>& tube,
-            T* events, size_t count) {
-        return recvObjects(tube, events, count, sizeof(T));
-    }
-
-    // parcels this BitTube
-    status_t writeToParcel(Parcel* reply) const;
-
-private:
-    void init(size_t rcvbuf, size_t sndbuf);
-
-    // send a message. The write is guaranteed to send the whole message or fail.
-    ssize_t write(void const* vaddr, size_t size);
-
-    // receive a message. the passed buffer must be at least as large as the
-    // write call used to send the message, excess data is silently discarded.
-    ssize_t read(void* vaddr, size_t size);
-
-    int mSendFd;
-    mutable int mReceiveFd;
-
-    static ssize_t sendObjects(const sp<BitTube>& tube,
-            void const* events, size_t count, size_t objSize);
-
-    static ssize_t recvObjects(const sp<BitTube>& tube,
-            void* events, size_t count, size_t objSize);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_CHANNEL_H
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index f45d852..55637a9 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -17,9 +17,7 @@
 #ifndef ANDROID_GUI_BUFFERITEM_H
 #define ANDROID_GUI_BUFFERITEM_H
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
+#include <ui/FenceTime.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -46,6 +44,8 @@
     enum { INVALID_BUFFER_SLOT = -1 };
     BufferItem();
     ~BufferItem();
+    BufferItem(const BufferItem&) = default;
+    BufferItem& operator=(const BufferItem&) = default;
 
     static const char* scalingModeName(uint32_t scalingMode);
 
@@ -57,6 +57,9 @@
     // mFence is a fence that will signal when the buffer is idle.
     sp<Fence> mFence;
 
+    // The std::shared_ptr<FenceTime> wrapper around mFence.
+    std::shared_ptr<FenceTime> mFenceTime{FenceTime::NO_FENCE};
+
     // mCrop is the current crop rectangle for this buffer slot.
     Rect mCrop;
 
@@ -72,13 +75,7 @@
     // to set by queueBuffer each time this slot is queued. This value
     // is guaranteed to be monotonically increasing for each newly
     // acquired buffer.
-    union {
-        int64_t mTimestamp;
-        struct {
-            uint32_t mTimestampLo;
-            uint32_t mTimestampHi;
-        };
-    };
+    int64_t mTimestamp;
 
     // mIsAutoTimestamp indicates whether mTimestamp was generated
     // automatically when the buffer was queued.
@@ -90,13 +87,7 @@
     android_dataspace mDataSpace;
 
     // mFrameNumber is the number of the queued frame for this slot.
-    union {
-        uint64_t mFrameNumber;
-        struct {
-            uint32_t mFrameNumberLo;
-            uint32_t mFrameNumberHi;
-        };
-    };
+    uint64_t mFrameNumber;
 
     // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
     int mSlot;
diff --git a/include/gui/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h
index 56c7a3f..db7e944 100644
--- a/include/gui/BufferItemConsumer.h
+++ b/include/gui/BufferItemConsumer.h
@@ -18,18 +18,13 @@
 #define ANDROID_GUI_BUFFERITEMCONSUMER_H
 
 #include <gui/ConsumerBase.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
+#include <gui/BufferQueue.h>
 
 #define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer"
 
 namespace android {
 
-class BufferQueue;
+class String8;
 
 /**
  * BufferItemConsumer is a BufferQueue consumer endpoint that allows clients
@@ -42,6 +37,10 @@
   public:
     typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
 
+    struct BufferFreedListener : public virtual RefBase {
+        virtual void onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) = 0;
+    };
+
     enum { DEFAULT_MAX_BUFFERS = -1 };
     enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
     enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
@@ -62,6 +61,10 @@
     // log messages.
     void setName(const String8& name);
 
+    // setBufferFreedListener sets the listener object that will be notified
+    // when an old buffer is being freed.
+    void setBufferFreedListener(const wp<BufferFreedListener>& listener);
+
     // Gets the next graphics buffer from the producer, filling out the
     // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue
     // of buffers is empty, and INVALID_OPERATION if the maximum number of
@@ -86,6 +89,13 @@
     status_t releaseBuffer(const BufferItem &item,
             const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
+   private:
+    void freeBufferLocked(int slotIndex) override;
+
+    // mBufferFreedListener is the listener object that will be called when
+    // an old buffer is being freed. If it is not NULL it will be called from
+    // freeBufferLocked.
+    wp<BufferFreedListener> mBufferFreedListener;
 };
 
 } // namespace android
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index fe4b1fa..bd62d85 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -23,10 +23,6 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IConsumerListener.h>
 
-// These are only required to keep other parts of the framework with incomplete
-// dependencies building successfully
-#include <gui/IGraphicBufferAlloc.h>
-
 namespace android {
 
 class BufferQueue {
@@ -60,14 +56,16 @@
     // weak references.
     class ProxyConsumerListener : public BnConsumerListener {
     public:
-        ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
+        explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
         virtual ~ProxyConsumerListener();
-        virtual void onFrameAvailable(const BufferItem& item) override;
-        virtual void onFrameReplaced(const BufferItem& item) override;
-        virtual void onBuffersReleased() override;
-        virtual void onSidebandStreamChanged() override;
-        virtual bool getFrameTimestamps(uint64_t frameNumber,
-                FrameTimestamps* outTimestamps) const override;
+        void onDisconnect() override;
+        void onFrameAvailable(const BufferItem& item) override;
+        void onFrameReplaced(const BufferItem& item) override;
+        void onBuffersReleased() override;
+        void onSidebandStreamChanged() override;
+        void addAndGetFrameTimestamps(
+                const NewFrameEventsEntry* newTimestamps,
+                FrameEventHistoryDelta* outDelta) override;
     private:
         // mConsumerListener is a weak reference to the IConsumerListener.  This is
         // the raison d'etre of ProxyConsumerListener.
@@ -79,10 +77,9 @@
     // needed gralloc buffers.
     static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
             sp<IGraphicBufferConsumer>* outConsumer,
-            const sp<IGraphicBufferAlloc>& allocator = NULL);
+            bool consumerIsSurfaceFlinger = false);
 
-private:
-    BufferQueue(); // Create through createBufferQueue
+    BufferQueue() = delete; // Create through createBufferQueue
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index 8ec0546..b383056 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -22,6 +22,7 @@
 
 #include <gui/BufferQueueDefs.h>
 #include <gui/IGraphicBufferConsumer.h>
+#include <utils/String8.h>
 
 namespace android {
 
@@ -109,7 +110,7 @@
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
 
     // setConsumerName sets the name used in logging
-    virtual void setConsumerName(const String8& name);
+    status_t setConsumerName(const String8& name) override;
 
     // setDefaultBufferFormat allows the BufferQueue to create
     // GraphicBuffers of a defaultFormat if no format is specified
@@ -128,13 +129,19 @@
     // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
     virtual status_t setConsumerUsageBits(uint32_t usage);
 
+    // setConsumerIsProtected will turn on an internal bit that indicates whether
+    // the consumer can handle protected gralloc buffers (i.e. with
+    // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this
+    // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED.
+    virtual status_t setConsumerIsProtected(bool isProtected);
+
     // setTransformHint bakes in rotation to buffers so overlays can be used.
     // The values are enumerated in window.h, e.g.
     // NATIVE_WINDOW_TRANSFORM_ROT_90.  The default is 0 (no transform).
     virtual status_t setTransformHint(uint32_t hint);
 
     // Retrieve the sideband buffer stream, if any.
-    virtual sp<NativeHandle> getSidebandStream() const;
+    status_t getSidebandStream(sp<NativeHandle>* outStream) const override;
 
     // See IGraphicBufferConsumer::getOccupancyHistory
     virtual status_t getOccupancyHistory(bool forceFlush,
@@ -144,7 +151,7 @@
     virtual status_t discardFreeBuffers() override;
 
     // dump our state in a String
-    virtual void dump(String8& result, const char* prefix) const;
+    status_t dumpState(const String8& prefix, String8* outResult) const override;
 
     // Functions required for backwards compatibility.
     // These will be modified/renamed in IGraphicBufferConsumer and will be
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 1226feb..dd8b992 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -51,7 +51,6 @@
 namespace android {
 
 class IConsumerListener;
-class IGraphicBufferAlloc;
 class IProducerListener;
 
 class BufferQueueCore : public virtual RefBase {
@@ -79,14 +78,13 @@
     typedef Vector<BufferItem> Fifo;
 
     // BufferQueueCore manages a pool of gralloc memory slots to be used by
-    // producers and consumers. allocator is used to allocate all the needed
-    // gralloc buffers.
-    BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
+    // producers and consumers.
+    BufferQueueCore();
     virtual ~BufferQueueCore();
 
 private:
     // Dump our state in a string
-    void dump(String8& result, const char* prefix) const;
+    void dumpState(const String8& prefix, String8* outResult) const;
 
     // getMinUndequeuedBufferCountLocked returns the minimum number of buffers
     // that must remain in a state other than DEQUEUED. The async parameter
@@ -143,10 +141,6 @@
     void validateConsistencyLocked() const;
 #endif
 
-    // mAllocator is the connection to SurfaceFlinger that is used to allocate
-    // new GraphicBuffer objects.
-    sp<IGraphicBufferAlloc> mAllocator;
-
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of BufferQueueCore objects. It must be locked whenever any
     // member variable is accessed.
@@ -178,6 +172,10 @@
     // GraphicBuffers.
     uint32_t mConsumerUsageBits;
 
+    // mConsumerIsProtected indicates the consumer is ready to handle protected
+    // buffer.
+    bool mConsumerIsProtected;
+
     // mConnectedApi indicates the producer API that is currently connected
     // to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
     // by the connect and disconnect methods.
@@ -317,13 +315,13 @@
 
     // Cached data about the shared buffer in shared buffer mode
     struct SharedBufferCache {
-        SharedBufferCache(Rect _crop, uint32_t _transform, int _scalingMode,
-                android_dataspace _dataspace)
+        SharedBufferCache(Rect _crop, uint32_t _transform,
+                uint32_t _scalingMode, android_dataspace _dataspace)
         : crop(_crop),
           transform(_transform),
           scalingMode(_scalingMode),
           dataspace(_dataspace) {
-        };
+        }
 
         Rect crop;
         uint32_t transform;
diff --git a/include/gui/BufferQueueDefs.h b/include/gui/BufferQueueDefs.h
index 83e9580..ffafb49 100644
--- a/include/gui/BufferQueueDefs.h
+++ b/include/gui/BufferQueueDefs.h
@@ -18,16 +18,12 @@
 #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
 
 #include <gui/BufferSlot.h>
+#include <ui/BufferQueueDefs.h>
 
 namespace android {
     class BufferQueueCore;
 
     namespace BufferQueueDefs {
-        // BufferQueue will keep track of at most this value of buffers.
-        // Attempts at runtime to increase the number of buffers past this
-        // will fail.
-        enum { NUM_BUFFER_SLOTS = 64 };
-
         typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
     } // namespace BufferQueueDefs
 } // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 8f613ee..5541468 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -22,14 +22,14 @@
 
 namespace android {
 
-class BufferSlot;
+struct BufferSlot;
 
 class BufferQueueProducer : public BnGraphicBufferProducer,
                             private IBinder::DeathRecipient {
 public:
     friend class BufferQueue; // Needed to access binderDied
 
-    BufferQueueProducer(const sp<BufferQueueCore>& core);
+    BufferQueueProducer(const sp<BufferQueueCore>& core, bool consumerIsSurfaceFlinger = false);
     virtual ~BufferQueueProducer();
 
     // requestBuffer returns the GraphicBuffer for slot N.
@@ -80,9 +80,9 @@
     //
     // 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,
+    status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
             uint32_t width, uint32_t height, PixelFormat format,
-            uint32_t usage);
+            uint32_t usage, FrameEventHistoryDelta* outTimestamps) override;
 
     // See IGraphicBufferProducer::detachBuffer
     virtual status_t detachBuffer(int slot);
@@ -177,8 +177,7 @@
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
 
     // See IGraphicBufferProducer::getFrameTimestamps
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const override;
+    virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
 
     // See IGraphicBufferProducer::getUniqueId
     virtual status_t getUniqueId(uint64_t* outId) const override;
@@ -195,6 +194,9 @@
     // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
     int getFreeSlotLocked() const;
 
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+            FrameEventHistoryDelta* outDelta);
+
     // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
     // block if there are no available slots and we are not in non-blocking
     // mode (producer and consumer controlled by the application). If it blocks,
@@ -218,6 +220,10 @@
 
     uint32_t mStickyTransform;
 
+    // This controls whether the GraphicBuffer pointer in the BufferItem is
+    // cleared after being queued
+    bool mConsumerIsSurfaceFlinger;
+
     // This saves the fence from the last queueBuffer, such that the
     // next queueBuffer call can throttle buffer production. The prior
     // queueBuffer's fence is not nessessarily available elsewhere,
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 0490c3c..891290b 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -17,19 +17,23 @@
 #ifndef ANDROID_GUI_CONSUMERBASE_H
 #define ANDROID_GUI_CONSUMERBASE_H
 
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/OccupancyTracker.h>
 
-#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
 
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
-#include <gui/IConsumerListener.h>
+
 
 namespace android {
 // ----------------------------------------------------------------------------
 
 class String8;
+class GraphicBuffer;
 
 // ConsumerBase is a base class for BufferQueue consumer end-points. It
 // handles common tasks like management of the connection to the BufferQueue
@@ -63,11 +67,11 @@
     // log messages.
     void setName(const String8& name);
 
-    // dump writes the current state to a string. Child classes should add
+    // dumpState writes the current state to a string. Child classes should add
     // their state to the dump by overriding the dumpLocked method, which is
     // called by these methods after locking the mutex.
-    void dump(String8& result) const;
-    void dump(String8& result, const char* prefix) const;
+    void dumpState(String8& result) const;
+    void dumpState(String8& result, const char* prefix) const;
 
     // setFrameAvailableListener sets the listener object that will be notified
     // when a new frame becomes available.
@@ -101,7 +105,7 @@
     // buffers from the given IGraphicBufferConsumer.
     // The controlledByApp flag indicates that this consumer is under the application's
     // control.
-    ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+    explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
 
     // onLastStrongRef gets called by RefBase just before the dtor of the most
     // derived class.  It is used to clean up the buffers so that ConsumerBase
@@ -180,7 +184,7 @@
     // Derived classes should override this method to perform any cleanup that
     // must take place when a buffer is released back to the BufferQueue.  If
     // it is overridden the derived class's implementation must call
-    // ConsumerBase::releaseBufferLocked.e
+    // ConsumerBase::releaseBufferLocked.
     virtual status_t releaseBufferLocked(int slot,
             const sp<GraphicBuffer> graphicBuffer,
             EGLDisplay display, EGLSyncKHR eglFence);
@@ -222,7 +226,7 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
-    Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
+    Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // mAbandoned indicates that the BufferQueue will no longer be used to
     // consume images buffers pushed to it using the IGraphicBufferProducer
@@ -237,13 +241,19 @@
 
     // mFrameAvailableListener is the listener object that will be called when a
     // new frame becomes available. If it is not NULL it will be called from
-    // queueBuffer.
+    // queueBuffer. The listener object is protected by mFrameAvailableMutex
+    // (not mMutex).
+    Mutex mFrameAvailableMutex;
     wp<FrameAvailableListener> mFrameAvailableListener;
 
     // The ConsumerBase has-a BufferQueue and is responsible for creating this object
     // if none is supplied
     sp<IGraphicBufferConsumer> mConsumer;
 
+    // The final release fence of the most recent buffer released by
+    // releaseBufferLocked.
+    sp<Fence> mPrevFinalReleaseFence;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of ConsumerBase objects. It must be locked whenever the
     // member variables are accessed or when any of the *Locked methods are
diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h
index b7aa463..58602bf 100644
--- a/include/gui/CpuConsumer.h
+++ b/include/gui/CpuConsumer.h
@@ -17,18 +17,19 @@
 #ifndef ANDROID_GUI_CPUCONSUMER_H
 #define ANDROID_GUI_CPUCONSUMER_H
 
+#include <system/window.h>
+
 #include <gui/ConsumerBase.h>
+#include <gui/BufferQueue.h>
 
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
 #include <utils/Vector.h>
-#include <utils/threads.h>
 
 
 namespace android {
 
 class BufferQueue;
+class GraphicBuffer;
+class String8;
 
 /**
  * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
index a4718b9..32ce59a 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -25,6 +25,7 @@
 #include <utils/Timers.h>
 
 #include <binder/IInterface.h>
+#include <gui/ISurfaceComposer.h>
 
 // ----------------------------------------------------------------------------
 
@@ -32,16 +33,25 @@
 
 // ----------------------------------------------------------------------------
 
-class BitTube;
 class IDisplayEventConnection;
 
-// ----------------------------------------------------------------------------
+namespace gui {
+class BitTube;
+} // namespace gui
 
+static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) {
+    return static_cast<uint32_t>(c1) << 24 |
+        static_cast<uint32_t>(c2) << 16 |
+        static_cast<uint32_t>(c3) << 8 |
+        static_cast<uint32_t>(c4);
+}
+
+// ----------------------------------------------------------------------------
 class DisplayEventReceiver {
 public:
     enum {
-        DISPLAY_EVENT_VSYNC = 'vsyn',
-        DISPLAY_EVENT_HOTPLUG = 'plug'
+        DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
+        DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
     };
 
     struct Event {
@@ -74,7 +84,8 @@
      * or requestNextVsync to receive them.
      * Other events start being delivered immediately.
      */
-    DisplayEventReceiver();
+    DisplayEventReceiver(
+            ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
 
     /*
      * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
@@ -102,15 +113,13 @@
      * should be destroyed and getEvents() shouldn't be called again.
      */
     ssize_t getEvents(Event* events, size_t count);
-    static ssize_t getEvents(const sp<BitTube>& dataChannel,
-            Event* events, size_t count);
+    static ssize_t getEvents(gui::BitTube* dataChannel, Event* events, size_t count);
 
     /*
      * sendEvents write events to the queue and returns how many events were
      * written.
      */
-    static ssize_t sendEvents(const sp<BitTube>& dataChannel,
-            Event const* events, size_t count);
+    static ssize_t sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count);
 
     /*
      * setVsyncRate() sets the Event::VSync delivery rate. A value of
@@ -128,7 +137,7 @@
 
 private:
     sp<IDisplayEventConnection> mEventConnection;
-    sp<BitTube> mDataChannel;
+    std::unique_ptr<gui::BitTube> mDataChannel;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..9716be4 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,321 @@
 #ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
 #define ANDROID_GUI_FRAMETIMESTAMPS_H
 
-#include <utils/Timers.h>
+#include <ui/FenceTime.h>
 #include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <array>
+#include <bitset>
+#include <vector>
 
 namespace android {
 
-struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
-    FrameTimestamps() :
-        frameNumber(0),
-        postedTime(0),
-        acquireTime(0),
-        refreshStartTime(0),
-        glCompositionDoneTime(0),
-        displayRetireTime(0),
-        releaseTime(0) {}
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
 
-    uint64_t frameNumber;
-    nsecs_t postedTime;
-    nsecs_t acquireTime;
-    nsecs_t refreshStartTime;
-    nsecs_t glCompositionDoneTime;
-    nsecs_t displayRetireTime;
-    nsecs_t releaseTime;
+
+// Identifiers for all the events that may be recorded or reported.
+enum class FrameEvent {
+    POSTED,
+    REQUESTED_PRESENT,
+    LATCH,
+    ACQUIRE,
+    FIRST_REFRESH_START,
+    LAST_REFRESH_START,
+    GPU_COMPOSITION_DONE,
+    DISPLAY_PRESENT,
+    DEQUEUE_READY,
+    RELEASE,
+    EVENT_COUNT, // Not an actual event.
 };
 
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+    static constexpr auto EVENT_COUNT =
+            static_cast<size_t>(FrameEvent::EVENT_COUNT);
+    static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+    static constexpr nsecs_t TIMESTAMP_PENDING = -2;
+
+    static inline bool isValidTimestamp(nsecs_t time) {
+        return time != TIMESTAMP_PENDING;
+    }
+
+    bool hasPostedInfo() const;
+    bool hasRequestedPresentInfo() const;
+    bool hasLatchInfo() const;
+    bool hasFirstRefreshStartInfo() const;
+    bool hasLastRefreshStartInfo() const;
+    bool hasAcquireInfo() const;
+    bool hasGpuCompositionDoneInfo() const;
+    bool hasDisplayPresentInfo() const;
+    bool hasReleaseInfo() const;
+    bool hasDequeueReadyInfo() const;
+
+    void checkFencesForCompletion();
+    void dump(String8& outString) const;
+
+    bool valid{false};
+    int connectId{0};
+    uint64_t frameNumber{0};
+
+    // Whether or not certain points in the frame's life cycle have been
+    // encountered help us determine if timestamps aren't available because
+    // a) we'll just never get them or b) they're not ready yet.
+    bool addPostCompositeCalled{false};
+    bool addReleaseCalled{false};
+
+    nsecs_t postedTime{TIMESTAMP_PENDING};
+    nsecs_t requestedPresentTime{TIMESTAMP_PENDING};
+    nsecs_t latchTime{TIMESTAMP_PENDING};
+    nsecs_t firstRefreshStartTime{TIMESTAMP_PENDING};
+    nsecs_t lastRefreshStartTime{TIMESTAMP_PENDING};
+    nsecs_t dequeueReadyTime{TIMESTAMP_PENDING};
+
+    std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> displayPresentFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
+};
+
+struct CompositorTiming {
+    nsecs_t deadline{0};
+    nsecs_t interval{16666667};
+    nsecs_t presentLatency{16666667};
+};
+
+// A short history of frames that are synchronized between the consumer and
+// producer via deltas.
+class FrameEventHistory {
+public:
+    virtual ~FrameEventHistory();
+
+    FrameEvents* getFrame(uint64_t frameNumber);
+    FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
+    void checkFencesForCompletion();
+    void dump(String8& outString) const;
+
+    static constexpr size_t MAX_FRAME_HISTORY = 8;
+
+protected:
+    std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+
+    CompositorTiming mCompositorTiming;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+    ~ProducerFrameEventHistory() override;
+
+    // Public for testing.
+    static nsecs_t snapToNextTick(
+            nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
+
+    nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
+    nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
+    nsecs_t getCompositeToPresentLatency() const {
+        return mCompositorTiming.presentLatency;
+    }
+
+    // virtual for testing.
+    virtual void updateAcquireFence(
+            uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire);
+    void applyDelta(const FrameEventHistoryDelta& delta);
+
+    void updateSignalTimes();
+
+protected:
+    void applyFenceDelta(FenceTimeline* timeline,
+            std::shared_ptr<FenceTime>* dst,
+            const FenceTime::Snapshot& src) const;
+
+    // virtual for testing.
+    virtual std::shared_ptr<FenceTime> createFenceTime(
+            const sp<Fence>& fence) const;
+
+    size_t mAcquireOffset{0};
+
+    // The consumer updates it's timelines in Layer and SurfaceFlinger since
+    // they can coordinate shared timelines better. The producer doesn't have
+    // shared timelines though, so just let it own and update all of them.
+    FenceTimeline mAcquireTimeline;
+    FenceTimeline mGpuCompositionDoneTimeline;
+    FenceTimeline mPresentTimeline;
+    FenceTimeline mReleaseTimeline;
+};
+
+
+// Used by the consumer to create a new frame event record that is
+// partially complete.
+struct NewFrameEventsEntry {
+    uint64_t frameNumber{0};
+    nsecs_t postedTime{0};
+    nsecs_t requestedPresentTime{0};
+    std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+};
+
+
+// Used by the consumer to keep track of which fields it already sent to
+// the producer.
+class FrameEventDirtyFields {
+public:
+    inline void reset() { mBitset.reset(); }
+    inline bool anyDirty() const { return mBitset.any(); }
+
+    template <FrameEvent event>
+    inline void setDirty() {
+        constexpr size_t eventIndex = static_cast<size_t>(event);
+        static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+        mBitset.set(eventIndex);
+    }
+
+    template <FrameEvent event>
+    inline bool isDirty() const {
+        constexpr size_t eventIndex = static_cast<size_t>(event);
+        static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+        return mBitset[eventIndex];
+    }
+
+private:
+    std::bitset<FrameEvents::EVENT_COUNT> mBitset;
+};
+
+
+// The consumer's interface to FrameEventHistory
+class ConsumerFrameEventHistory : public FrameEventHistory {
+public:
+    ~ConsumerFrameEventHistory() override;
+
+    void onDisconnect();
+
+    void initializeCompositorTiming(const CompositorTiming& compositorTiming);
+
+    void addQueue(const NewFrameEventsEntry& newEntry);
+    void addLatch(uint64_t frameNumber, nsecs_t latchTime);
+    void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime);
+    void addPostComposition(uint64_t frameNumber,
+            const std::shared_ptr<FenceTime>& gpuCompositionDone,
+            const std::shared_ptr<FenceTime>& displayPresent,
+            const CompositorTiming& compositorTiming);
+    void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime,
+            std::shared_ptr<FenceTime>&& release);
+
+    void getAndResetDelta(FrameEventHistoryDelta* delta);
+
+private:
+    void getFrameDelta(FrameEventHistoryDelta* delta,
+            const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame);
+
+    std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+
+    size_t mQueueOffset{0};
+    size_t mCompositionOffset{0};
+    size_t mReleaseOffset{0};
+
+    int mCurrentConnectId{0};
+    bool mProducerWantsEvents{false};
+};
+
+
+// A single frame update from the consumer to producer that can be sent
+// through Binder.
+// Although this may be sent multiple times for the same frame as new
+// timestamps are set, Fences only need to be sent once.
+class FrameEventsDelta : public Flattenable<FrameEventsDelta> {
+friend class ProducerFrameEventHistory;
+public:
+    FrameEventsDelta() = default;
+    FrameEventsDelta(size_t index,
+            const FrameEvents& frameTimestamps,
+            const FrameEventDirtyFields& dirtyFields);
+
+    // Movable.
+    FrameEventsDelta(FrameEventsDelta&& src) = default;
+    FrameEventsDelta& operator=(FrameEventsDelta&& src) = default;
+    // Not copyable.
+    FrameEventsDelta(const FrameEventsDelta& src) = delete;
+    FrameEventsDelta& operator=(const FrameEventsDelta& src) = delete;
+
+    // Flattenable implementation
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void*& buffer, size_t& size, int*& fds,
+            size_t& count) const;
+    status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+            size_t& count);
+
+private:
+    static constexpr size_t minFlattenedSize();
+
+    size_t mIndex{0};
+    uint64_t mFrameNumber{0};
+
+    bool mAddPostCompositeCalled{0};
+    bool mAddReleaseCalled{0};
+
+    nsecs_t mPostedTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mRequestedPresentTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mLatchTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mFirstRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mLastRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mDequeueReadyTime{FrameEvents::TIMESTAMP_PENDING};
+
+    FenceTime::Snapshot mGpuCompositionDoneFence;
+    FenceTime::Snapshot mDisplayPresentFence;
+    FenceTime::Snapshot mReleaseFence;
+
+    // This is a static method with an auto return value so we can call
+    // it without needing const and non-const versions.
+    template <typename ThisT>
+    static inline auto allFences(ThisT fed) ->
+            std::array<decltype(&fed->mReleaseFence), 3> {
+        return {{
+            &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence,
+            &fed->mReleaseFence
+        }};
+    }
+};
+
+
+// A collection of updates from consumer to producer that can be sent
+// through Binder.
+class FrameEventHistoryDelta
+        : public Flattenable<FrameEventHistoryDelta> {
+
+friend class ConsumerFrameEventHistory;
+friend class ProducerFrameEventHistory;
+
+public:
+    FrameEventHistoryDelta() = default;
+
+    // Movable.
+    FrameEventHistoryDelta(FrameEventHistoryDelta&& src) = default;
+    FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src);
+    // Not copyable.
+    FrameEventHistoryDelta(const FrameEventHistoryDelta& src) = delete;
+    FrameEventHistoryDelta& operator=(
+            const FrameEventHistoryDelta& src) = delete;
+
+    // Flattenable implementation.
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void*& buffer, size_t& size, int*& fds,
+            size_t& count) const;
+    status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+            size_t& count);
+
+private:
+    static constexpr size_t minFlattenedSize();
+
+    std::vector<FrameEventsDelta> mDeltas;
+    CompositorTiming mCompositorTiming;
+};
+
+
 } // namespace android
 #endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..2cf6162 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -20,10 +20,10 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
 
+#include <ui/FenceTime.h>
 #include <ui/GraphicBuffer.h>
 
 #include <utils/String8.h>
@@ -146,6 +146,10 @@
     // documented by the source.
     int64_t getTimestamp();
 
+    // getDataSpace retrieves the DataSpace associated with the texture image
+    // set by the most recent call to updateTexImage.
+    android_dataspace getCurrentDataSpace();
+
     // getFrameNumber retrieves the frame number associated with the texture
     // image set by the most recent call to updateTexImage.
     //
@@ -168,7 +172,9 @@
     void setFilteringEnabled(bool enabled);
 
     // getCurrentBuffer returns the buffer associated with the current image.
-    sp<GraphicBuffer> getCurrentBuffer() const;
+    // When outSlot is not nullptr, the current buffer slot index is also
+    // returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
 
     // getCurrentTextureTarget returns the texture target of the current
     // texture as returned by updateTexImage().
@@ -187,6 +193,10 @@
     // ready to be read from.
     sp<Fence> getCurrentFence() const;
 
+    // getCurrentFence returns the FenceTime indicating when the current
+    // buffer is ready to be read from.
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
     // doGLFenceWait inserts a wait command into the OpenGL ES command stream
     // to ensure that it is safe for future OpenGL ES commands to access the
     // current texture buffer.
@@ -250,7 +260,7 @@
     // mEglSlots array in addition to the ConsumerBase.
     virtual status_t releaseBufferLocked(int slot,
             const sp<GraphicBuffer> graphicBuffer,
-            EGLDisplay display, EGLSyncKHR eglFence);
+            EGLDisplay display, EGLSyncKHR eglFence) override;
 
     status_t releaseBufferLocked(int slot,
             const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
@@ -398,6 +408,9 @@
     // mCurrentFence is the fence received from BufferQueue in updateTexImage.
     sp<Fence> mCurrentFence;
 
+    // The FenceTime wrapper around mCurrentFence.
+    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
     // mCurrentTransformMatrix is the transform matrix for the current texture.
     // It gets computed by computeTransformMatrix each time updateTexImage is
     // called.
@@ -407,6 +420,10 @@
     // gets set each time updateTexImage is called.
     int64_t mCurrentTimestamp;
 
+    // mCurrentDataSpace is the dataspace for the current texture. It
+    // gets set each time updateTexImage is called.
+    android_dataspace mCurrentDataSpace;
+
     // mCurrentFrameNumber is the frame counter for the current texture.
     // It gets set each time updateTexImage is called.
     uint64_t mCurrentFrameNumber;
@@ -472,7 +489,7 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
-    EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
+    EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
deleted file mode 100644
index 62e3877..0000000
--- a/include/gui/GraphicBufferAlloc.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
-#define ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <gui/IGraphicBufferAlloc.h>
-#include <ui/PixelFormat.h>
-#include <utils/Errors.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GraphicBuffer;
-
-class GraphicBufferAlloc : public BnGraphicBufferAlloc {
-public:
-    GraphicBufferAlloc();
-    virtual ~GraphicBufferAlloc();
-    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
-            std::string requestorName, status_t* error) override;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
diff --git a/include/gui/GraphicsEnv.h b/include/gui/GraphicsEnv.h
deleted file mode 100644
index 4c7366f..0000000
--- a/include/gui/GraphicsEnv.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_GUI_GRAPHICS_ENV_H
-#define ANDROID_GUI_GRAPHICS_ENV_H 1
-
-#include <string>
-
-struct android_namespace_t;
-
-namespace android {
-
-class GraphicsEnv {
-public:
-    static GraphicsEnv& getInstance();
-
-    // Set a search path for loading graphics drivers. The path is a list of
-    // directories separated by ':'. A directory can be contained in a zip file
-    // (drivers must be stored uncompressed and page aligned); such elements
-    // in the search path must have a '!' after the zip filename, e.g.
-    //     /data/app/com.example.driver/base.apk!/lib/arm64-v8a
-    void setDriverPath(const std::string path);
-    android_namespace_t* getDriverNamespace();
-
-private:
-    GraphicsEnv() = default;
-    std::string mDriverPath;
-    android_namespace_t* mDriverNamespace = nullptr;
-};
-
-} // namespace android
-
-/* FIXME
- * Export an un-mangled function that just does
- *     return android::GraphicsEnv::getInstance().getDriverNamespace();
- * This allows libEGL to get the function pointer via dlsym, since it can't
- * directly link against libgui. In a future release, we'll fix this so that
- * libgui does not depend on graphics API libraries, and libEGL can link
- * against it. The current dependencies from libgui -> libEGL are:
- *  - the GLConsumer class, which should be moved to its own library
- *  - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
- *    will be removed soon.
- */
-extern "C" android_namespace_t* android_getDriverNamespace();
-
-#endif // ANDROID_GUI_GRAPHICS_ENV_H
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 1efcf3c..c082882 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -14,98 +14,84 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_ICONSUMERLISTENER_H
-#define ANDROID_GUI_ICONSUMERLISTENER_H
+#pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
-#include <binder/IInterface.h>
-
-#include <gui/FrameTimestamps.h>
+#include <cstdint>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
 class BufferItem;
+class FrameEventHistoryDelta;
+struct NewFrameEventsEntry;
 
-// ConsumerListener is the interface through which the BufferQueue notifies
-// the consumer of events that the consumer may wish to react to.  Because
-// the consumer will generally have a mutex that is locked during calls from
-// the consumer to the BufferQueue, these calls from the BufferQueue to the
+// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
+// that the consumer may wish to react to. Because the consumer will generally have a mutex that is
+// locked during calls from the consumer to the BufferQueue, these calls from the BufferQueue to the
 // consumer *MUST* be called only when the BufferQueue mutex is NOT locked.
 
 class ConsumerListener : public virtual RefBase {
 public:
-    ConsumerListener() { }
-    virtual ~ConsumerListener() { }
+    ConsumerListener() {}
+    virtual ~ConsumerListener();
 
-    // onFrameAvailable is called from queueBuffer each time an additional
-    // frame becomes available for consumption. This means that frames that
-    // are queued while in asynchronous mode only trigger the callback if no
-    // previous frames are pending. Frames queued while in synchronous mode
-    // always trigger the callback. The item passed to the callback will contain
-    // all of the information about the queued frame except for its
-    // GraphicBuffer pointer, which will always be null.
+    // onDisconnect is called when a producer disconnects from the BufferQueue.
+    virtual void onDisconnect() {} /* Asynchronous */
+
+    // onFrameAvailable is called from queueBuffer each time an additional frame becomes available
+    // for consumption. This means that frames that are queued while in asynchronous mode only
+    // trigger the callback if no previous frames are pending. Frames queued while in synchronous
+    // mode always trigger the callback. The item passed to the callback will contain all of the
+    // information about the queued frame except for its GraphicBuffer pointer, which will always be
+    // null (except if the consumer is SurfaceFlinger).
     //
-    // This is called without any lock held and can be called concurrently
-    // by multiple threads.
+    // This is called without any lock held and can be called concurrently by multiple threads.
     virtual void onFrameAvailable(const BufferItem& item) = 0; /* Asynchronous */
 
-    // onFrameReplaced is called from queueBuffer if the frame being queued is
-    // replacing an existing slot in the queue. Any call to queueBuffer that
-    // doesn't call onFrameAvailable will call this callback instead. The item
-    // passed to the callback will contain all of the information about the
-    // queued frame except for its GraphicBuffer pointer, which will always be
-    // null.
+    // onFrameReplaced is called from queueBuffer if the frame being queued is replacing an existing
+    // slot in the queue. Any call to queueBuffer that doesn't call onFrameAvailable will call this
+    // callback instead. The item passed to the callback will contain all of the information about
+    // the queued frame except for its GraphicBuffer pointer, which will always be null.
     //
-    // This is called without any lock held and can be called concurrently
-    // by multiple threads.
+    // This is called without any lock held and can be called concurrently by multiple threads.
     virtual void onFrameReplaced(const BufferItem& /* item */) {} /* Asynchronous */
 
-    // onBuffersReleased is called to notify the buffer consumer that the
-    // BufferQueue has released its references to one or more GraphicBuffers
-    // contained in its slots.  The buffer consumer should then call
-    // BufferQueue::getReleasedBuffers to retrieve the list of buffers
+    // onBuffersReleased is called to notify the buffer consumer that the BufferQueue has released
+    // its references to one or more GraphicBuffers contained in its slots. The buffer consumer
+    // should then call BufferQueue::getReleasedBuffers to retrieve the list of buffers.
     //
-    // This is called without any lock held and can be called concurrently
-    // by multiple threads.
+    // This is called without any lock held and can be called concurrently by multiple threads.
     virtual void onBuffersReleased() = 0; /* Asynchronous */
 
-    // onSidebandStreamChanged is called to notify the buffer consumer that the
-    // BufferQueue's sideband buffer stream has changed. This is called when a
-    // stream is first attached and when it is either detached or replaced by a
-    // different stream.
+    // onSidebandStreamChanged is called to notify the buffer consumer that the BufferQueue's
+    // sideband buffer stream has changed. This is called when a stream is first attached and when
+    // it is either detached or replaced by a different stream.
     virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
 
-    // See IGraphicBufferProducer::getFrameTimestamps
-    // This queries the consumer for the timestamps
-    virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
-            FrameTimestamps* /*outTimestamps*/) const { return false; }
+    // Notifies the consumer of any new producer-side timestamps and returns the combined frame
+    // history that hasn't already been retrieved.
+    //
+    // WARNING: This method can only be called when the BufferQueue is in the consumer's process.
+    virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
+                                          FrameEventHistoryDelta* /*outDelta*/) {}
 };
 
-
-class IConsumerListener : public ConsumerListener, public IInterface
-{
+class IConsumerListener : public ConsumerListener, public IInterface {
 public:
-    DECLARE_META_INTERFACE(ConsumerListener);
+    DECLARE_META_INTERFACE(ConsumerListener)
 };
 
-// ----------------------------------------------------------------------------
-
-class BnConsumerListener : public BnInterface<IConsumerListener>
-{
+class BnConsumerListener : public SafeBnInterface<IConsumerListener> {
 public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
+    BnConsumerListener() : SafeBnInterface<IConsumerListener>("BnConsumerListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
 };
 
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ICONSUMERLISTENER_H
+} // namespace android
diff --git a/include/gui/IDisplayEventConnection.h b/include/gui/IDisplayEventConnection.h
index 86247de..d783f74 100644
--- a/include/gui/IDisplayEventConnection.h
+++ b/include/gui/IDisplayEventConnection.h
@@ -14,60 +14,52 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
-#define ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
+#pragma once
 
 #include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+#include <utils/Errors.h>
+
+#include <cstdint>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
+namespace gui {
 class BitTube;
+} // namespace gui
 
-class IDisplayEventConnection : public IInterface
-{
+class IDisplayEventConnection : public IInterface {
 public:
-
-    DECLARE_META_INTERFACE(DisplayEventConnection);
+    DECLARE_META_INTERFACE(DisplayEventConnection)
 
     /*
-     * getDataChannel() returns a BitTube where to receive the events from
+     * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
+     * descriptor of outChannel will be initialized, and this effectively "steals" the receive
+     * channel from the remote end (such that the remote end can only use its send channel).
      */
-    virtual sp<BitTube> getDataChannel() const = 0;
+    virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
 
     /*
-     * setVsyncRate() sets the vsync event delivery rate. A value of
-     * 1 returns every vsync events. A value of 2 returns every other events,
-     * etc... a value of 0 returns no event unless  requestNextVsync() has
-     * been called.
+     * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
+     * A value of 2 returns every other event, etc. A value of 0 returns no event unless
+     * requestNextVsync() has been called.
      */
-    virtual void setVsyncRate(uint32_t count) = 0;
+    virtual status_t setVsyncRate(uint32_t count) = 0;
 
     /*
-     * requestNextVsync() schedules the next vsync event. It has no effect
-     * if the vsync rate is > 0.
+     * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
      */
-    virtual void requestNextVsync() = 0;    // asynchronous
+    virtual void requestNextVsync() = 0; // Asynchronous
 };
 
-// ----------------------------------------------------------------------------
-
-class BnDisplayEventConnection : public BnInterface<IDisplayEventConnection>
-{
+class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
 public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
+    BnDisplayEventConnection()
+          : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
 };
 
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
+} // namespace android
diff --git a/include/gui/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h
deleted file mode 100644
index 600cf27..0000000
--- a/include/gui/IGraphicBufferAlloc.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
-#define ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-#include <utils/RefBase.h>
-
-#include <string>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class IGraphicBufferAlloc : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(GraphicBufferAlloc);
-
-    /* Create a new GraphicBuffer for the client to use.
-     */
-    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t usage, std::string requestorName,
-            status_t* error) = 0;
-
-    sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t usage, status_t* error) {
-        return createGraphicBuffer(w, h, format, usage, "<Unknown>", error);
-    }
-};
-
-// ----------------------------------------------------------------------------
-
-class BnGraphicBufferAlloc : public BnInterface<IGraphicBufferAlloc>
-{
-public:
-    virtual status_t onTransact(uint32_t code,
-                                const Parcel& data,
-                                Parcel* reply,
-                                uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_IGRAPHIC_BUFFER_ALLOC_H
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index 3b10d78..3d069df 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -14,26 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
-#define ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
+#pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
+#include <gui/OccupancyTracker.h>
 
 #include <binder/IInterface.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <gui/OccupancyTracker.h>
+#include <binder/SafeInterface.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
+#include <ui/PixelFormat.h>
+
+#include <utils/Errors.h>
+
 namespace android {
-// ----------------------------------------------------------------------------
 
 class BufferItem;
 class Fence;
@@ -42,11 +37,12 @@
 class NativeHandle;
 
 class IGraphicBufferConsumer : public IInterface {
-
 public:
+    DECLARE_META_INTERFACE(GraphicBufferConsumer)
+
     enum {
-        // Returned by releaseBuffer, after which the consumer must
-        // free any references to the just-released buffer that it might have.
+        // Returned by releaseBuffer, after which the consumer must free any references to the
+        // just-released buffer that it might have.
         STALE_BUFFER_SLOT = 1,
         // Returned by dequeueBuffer if there are no pending buffers available.
         NO_BUFFER_AVAILABLE,
@@ -54,88 +50,79 @@
         PRESENT_LATER,
     };
 
-    // acquireBuffer attempts to acquire ownership of the next pending buffer in
-    // the BufferQueue.  If no buffer is pending then it returns
-    // NO_BUFFER_AVAILABLE.  If a buffer is successfully acquired, the
-    // information about the buffer is returned in BufferItem.
+    // acquireBuffer attempts to acquire ownership of the next pending buffer in the BufferQueue.
+    // If no buffer is pending then it returns NO_BUFFER_AVAILABLE. If a buffer is successfully
+    // acquired, the information about the buffer is returned in BufferItem.
     //
-    // If the buffer returned had previously been
-    // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
-    // NULL and it is assumed that the consumer still holds a reference to the
+    // If the buffer returned had previously been acquired then the BufferItem::mGraphicBuffer field
+    // of buffer is set to NULL and it is assumed that the consumer still holds a reference to the
     // buffer.
     //
-    // If presentWhen is non-zero, it indicates the time when the buffer will
-    // be displayed on screen.  If the buffer's timestamp is farther in the
-    // future, the buffer won't be acquired, and PRESENT_LATER will be
-    // returned.  The presentation time is in nanoseconds, and the time base
+    // If presentWhen is non-zero, it indicates the time when the buffer will be displayed on
+    // screen. If the buffer's timestamp is farther in the future, the buffer won't be acquired, and
+    // PRESENT_LATER will be returned. The presentation time is in nanoseconds, and the time base
     // is CLOCK_MONOTONIC.
     //
-    // If maxFrameNumber is non-zero, it indicates that acquireBuffer should
-    // only return a buffer with a frame number less than or equal to
-    // maxFrameNumber. If no such frame is available (such as when a buffer has
-    // been replaced but the consumer has not received the onFrameReplaced
-    // callback), then PRESENT_LATER will be returned.
+    // If maxFrameNumber is non-zero, it indicates that acquireBuffer should only return a buffer
+    // with a frame number less than or equal to maxFrameNumber. If no such frame is available
+    // (such as when a buffer has been replaced but the consumer has not received the
+    // onFrameReplaced callback), then PRESENT_LATER will be returned.
     //
     // Return of NO_ERROR means the operation completed as normal.
     //
-    // Return of a positive value means the operation could not be completed
-    //    at this time, but the user should try again later:
+    // Return of a positive value means the operation could not be completed at this time, but the
+    // user should try again later:
     // * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer)
     // * PRESENT_LATER - the buffer's timestamp is farther in the future
     //
     // Return of a negative value means an error has occurred:
     // * INVALID_OPERATION - too many buffers have been acquired
     virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
-            uint64_t maxFrameNumber = 0) = 0;
+                                   uint64_t maxFrameNumber = 0) = 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
-    // freed, and there will be no way to obtain the buffer from this interface.
-    // The freed slot will remain unallocated until either it is selected to
-    // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
-    // to the slot. The buffer must have already been acquired.
+    // 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 freed, and there will be no way to obtain the
+    // buffer from this interface. The freed slot will remain unallocated until either it is
+    // selected to hold a freshly allocated buffer in dequeueBuffer or a buffer is attached to the
+    // slot. The buffer must have already been acquired.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - the given slot number is invalid, either because it is
-    //               out of the range [0, NUM_BUFFER_SLOTS) or because the slot
-    //               it refers to is not currently acquired.
+    // * BAD_VALUE - the given slot number is invalid, either because it is out of the range
+    //               [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+    //               currently acquired.
     virtual status_t detachBuffer(int slot) = 0;
 
-    // attachBuffer attempts to transfer ownership of a buffer to the buffer
-    // queue. If this call succeeds, it will be as if this buffer was acquired
-    // from the returned slot number. As such, this call will fail if attaching
-    // this buffer would cause too many buffers to be simultaneously acquired.
+    // attachBuffer attempts to transfer ownership of a buffer to the BufferQueue. If this call
+    // succeeds, it will be as if this buffer was acquired from the returned slot number. As such,
+    // this call will fail if attaching this buffer would cause too many buffers to be
+    // simultaneously acquired.
     //
-    // If the buffer is successfully attached, its frameNumber is initialized
-    // to 0. This must be passed into the releaseBuffer call or else the buffer
-    // will be deallocated as stale.
+    // If the buffer is successfully attached, its frameNumber is initialized to 0. This must be
+    // passed into the releaseBuffer call or else the buffer will be deallocated as stale.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - outSlot or buffer were NULL, or the generation number of
-    //               the buffer did not match the buffer queue.
-    // * INVALID_OPERATION - cannot attach the buffer because it would cause too
-    //                       many buffers to be acquired.
+    // * BAD_VALUE - outSlot or buffer were NULL, or the generation number of the buffer did not
+    //               match the BufferQueue.
+    // * INVALID_OPERATION - cannot attach the buffer because it would cause too many buffers
+    //                       to be acquired.
     // * NO_MEMORY - no free slots available
-    virtual status_t attachBuffer(int *outSlot,
-            const sp<GraphicBuffer>& buffer) = 0;
+    virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) = 0;
 
-    // releaseBuffer releases a buffer slot from the consumer back to the
-    // BufferQueue.  This may be done while the buffer's contents are still
-    // being accessed.  The fence will signal when the buffer is no longer
-    // in use. frameNumber is used to indentify the exact buffer returned.
+    // releaseBuffer releases a buffer slot from the consumer back to the BufferQueue. This may be
+    // done while the buffer's contents are still being accessed. The fence will signal when the
+    // buffer is no longer in use. frameNumber is used to identify the exact buffer returned.
     //
-    // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
-    // any references to the just-released buffer that it might have, as if it
-    // had received a onBuffersReleased() call with a mask set for the released
-    // buffer.
+    // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free any references to the
+    // just-released buffer that it might have, as if it had received a onBuffersReleased() call
+    // with a mask set for the released buffer.
     //
-    // Note that the dependencies on EGL will be removed once we switch to using
-    // the Android HW Sync HAL.
+    // Note that the dependencies on EGL will be removed once we switch to using the Android HW
+    // Sync HAL.
     //
     // Return of NO_ERROR means the operation completed as normal.
     //
-    // Return of a positive value means the operation could not be completed
-    //    at this time, but the user should try again later:
+    // Return of a positive value means the operation could not be completed at this time, but the
+    // user should try again later:
     // * STALE_BUFFER_SLOT - see above (second paragraph)
     //
     // Return of a negative value means an error has occurred:
@@ -143,159 +130,165 @@
     //               * the buffer slot was invalid
     //               * the fence was NULL
     //               * the buffer slot specified is not in the acquired state
-    virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
-            EGLDisplay display, EGLSyncKHR fence,
-            const sp<Fence>& releaseFence) = 0;
+    virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display,
+                                   EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0;
 
-    // consumerConnect connects a consumer to the BufferQueue.  Only one
-    // consumer may be connected, and when that consumer disconnects the
-    // BufferQueue is placed into the "abandoned" state, causing most
-    // interactions with the BufferQueue by the producer to fail.
-    // controlledByApp indicates whether the consumer is controlled by
-    // the application.
+    status_t releaseHelper(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
+        return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
+    }
+    // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but:
+    //     1) We have no easy way to send the EGL objects across Binder
+    //     2) This has always been broken, probably because
+    //     3) IGBC is rarely remoted
+    // For now, we will choose to bury our heads in the sand and ignore this problem until such time
+    // as we can finally finish converting away from EGL sync to native Android sync
+    using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper);
+
+    // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected,
+    // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state,
+    // causing most interactions with the BufferQueue by the producer to fail. controlledByApp
+    // indicates whether the consumer is controlled by the application.
     //
     // consumer may not be NULL.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned
+    // * NO_INIT - the BufferQueue has been abandoned
     // * BAD_VALUE - a NULL consumer was provided
-    virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
+    virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
+                                     bool controlledByApp) = 0;
 
-    // consumerDisconnect disconnects a consumer from the BufferQueue. All
-    // buffers will be freed and the BufferQueue is placed in the "abandoned"
-    // state, causing most interactions with the BufferQueue by the producer to
-    // fail.
+    // consumerDisconnect disconnects a consumer from the BufferQueue. All buffers will be freed and
+    // the BufferQueue is placed in the "abandoned" state, causing most interactions with the
+    // BufferQueue by the producer to fail.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * BAD_VALUE - no consumer is currently connected
     virtual status_t consumerDisconnect() = 0;
 
-    // getReleasedBuffers sets the value pointed to by slotMask to a bit set.
-    // Each bit index with a 1 corresponds to a released buffer slot with that
-    // index value.  In particular, a released buffer is one that has
-    // been released by the BufferQueue but have not yet been released by the consumer.
+    // getReleasedBuffers sets the value pointed to by slotMask to a bit set. Each bit index with a
+    // 1 corresponds to a released buffer slot with that index value. In particular, a released
+    // buffer is one that has been released by the BufferQueue but has not yet been released by
+    // the consumer.
     //
     // This should be called from the onBuffersReleased() callback.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the BufferQueue has been abandoned.
     virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
 
-    // setDefaultBufferSize is used to set the size of buffers returned by
-    // dequeueBuffer when a width and height of zero is requested.  Default
-    // is 1x1.
+    // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
+    // width and height of zero is requested. Default is 1x1.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * BAD_VALUE - either w or h was zero
     virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
 
-    // setMaxBufferCount sets the maximum value for the number of buffers used
-    // in the buffer queue (the initial default is NUM_BUFFER_SLOTS). If a call
-    // to setMaxAcquiredBufferCount (by the consumer), or a call to setAsyncMode
-    // or setMaxDequeuedBufferCount (by the producer), would cause this value to
-    // be exceeded then that call will fail. This call will fail if a producer
+    // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
+    // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
+    // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
+    // cause this value to be exceeded then that call will fail. This call will fail if a producer
     // is connected to the BufferQueue.
     //
-    // The count must be between 1 and NUM_BUFFER_SLOTS, inclusive. The count
-    // cannot be less than maxAcquiredBufferCount.
+    // The count must be between 1 and NUM_BUFFER_SLOTS, inclusive. The count cannot be less than
+    // maxAcquiredBufferCount.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * BAD_VALUE - one of the below conditions occurred:
-    //             * bufferCount was out of range (see above).
-    //             * failure to adjust the number of available slots.
+    //               * bufferCount was out of range (see above).
+    //               * failure to adjust the number of available slots.
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxBufferCount(int bufferCount) = 0;
 
-    // setMaxAcquiredBufferCount sets the maximum number of buffers that can
-    // be acquired by the consumer at one time (default 1). If this method
-    // succeeds, any new buffer slots will be both unallocated and owned by the
-    // BufferQueue object (i.e. they are not owned by the producer or consumer).
-    // Calling this may also cause some buffer slots to be emptied.
+    // setMaxAcquiredBufferCount sets the maximum number of buffers that can be acquired by the
+    // consumer at one time (default 1). If this method succeeds, any new buffer slots will be both
+    // unallocated and owned by the BufferQueue object (i.e. they are not owned by the producer or
+    // consumer). Calling this may also cause some buffer slots to be emptied.
     //
-    // This function should not be called with a value of maxAcquiredBuffers
-    // that is less than the number of currently acquired buffer slots. Doing so
-    // will result in a BAD_VALUE error.
+    // This function should not be called with a value of maxAcquiredBuffers that is less than the
+    // number of currently acquired buffer slots. Doing so will result in a BAD_VALUE error.
     //
-    // maxAcquiredBuffers must be (inclusive) between 1 and
-    // MAX_MAX_ACQUIRED_BUFFERS. It also cannot cause the maxBufferCount value
-    // to be exceeded.
+    // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
+    // cause the maxBufferCount value to be exceeded.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned
+    // * NO_INIT - the BufferQueue has been abandoned
     // * BAD_VALUE - one of the below conditions occurred:
-    //             * maxAcquiredBuffers was out of range (see above).
-    //             * failure to adjust the number of available slots.
-    //             * client would have more than the requested number of
-    //               acquired buffers after this call
+    //               * maxAcquiredBuffers was out of range (see above).
+    //               * failure to adjust the number of available slots.
+    //               * client would have more than the requested number of acquired buffers after
+    //                 this call
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
 
     // setConsumerName sets the name used in logging
-    virtual void setConsumerName(const String8& name) = 0;
+    virtual status_t setConsumerName(const String8& name) = 0;
 
-    // setDefaultBufferFormat allows the BufferQueue to create
-    // GraphicBuffers of a defaultFormat if no format is specified
-    // in dequeueBuffer.
-    // The initial default is PIXEL_FORMAT_RGBA_8888.
+    // setDefaultBufferFormat allows the BufferQueue to create GraphicBuffers of a defaultFormat if
+    // no format is specified in dequeueBuffer. The initial default is PIXEL_FORMAT_RGBA_8888.
     //
     // Return of a value other than NO_ERROR means an unknown error has occurred.
     virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0;
 
-    // setDefaultBufferDataSpace is a request to the producer to provide buffers
-    // of the indicated dataSpace. The producer may ignore this request.
-    // The initial default is HAL_DATASPACE_UNKNOWN.
+    // setDefaultBufferDataSpace is a request to the producer to provide buffers of the indicated
+    // dataSpace. The producer may ignore this request. The initial default is
+    // HAL_DATASPACE_UNKNOWN.
     //
     // Return of a value other than NO_ERROR means an unknown error has occurred.
-    virtual status_t setDefaultBufferDataSpace(
-            android_dataspace defaultDataSpace) = 0;
+    virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0;
 
-    // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
-    // These are merged with the bits passed to dequeueBuffer.  The values are
-    // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+    // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. These are merged
+    // with the bits passed to dequeueBuffer. The values are enumerated in gralloc.h,
+    // e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
     //
     // Return of a value other than NO_ERROR means an unknown error has occurred.
     virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
 
-    // setTransformHint bakes in rotation to buffers so overlays can be used.
-    // The values are enumerated in window.h, e.g.
-    // NATIVE_WINDOW_TRANSFORM_ROT_90.  The default is 0 (no transform).
+    // setConsumerIsProtected will turn on an internal bit that indicates whether
+    // the consumer can handle protected gralloc buffers (i.e. with
+    // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this
+    // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED.
+    virtual status_t setConsumerIsProtected(bool isProtected) = 0;
+
+    // setTransformHint bakes in rotation to buffers so overlays can be used. The values are
+    // enumerated in window.h, e.g. NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0
+    // (no transform).
     //
     // Return of a value other than NO_ERROR means an unknown error has occurred.
     virtual status_t setTransformHint(uint32_t hint) = 0;
 
     // Retrieve the sideband buffer stream, if any.
-    virtual sp<NativeHandle> getSidebandStream() const = 0;
+    virtual status_t getSidebandStream(sp<NativeHandle>* outStream) const = 0;
 
-    // Retrieves any stored segments of the occupancy history of this
-    // BufferQueue and clears them. Optionally closes out the pending segment if
-    // forceFlush is true.
+    // Retrieves any stored segments of the occupancy history of this BufferQueue and clears them.
+    // Optionally closes out the pending segment if forceFlush is true.
     virtual status_t getOccupancyHistory(bool forceFlush,
-            std::vector<OccupancyTracker::Segment>* outHistory) = 0;
+                                         std::vector<OccupancyTracker::Segment>* outHistory) = 0;
 
-    // discardFreeBuffers releases all currently-free buffers held by the queue,
-    // in order to reduce the memory consumption of the queue to the minimum
-    // possible without discarding data.
+    // discardFreeBuffers releases all currently-free buffers held by the BufferQueue, in order to
+    // reduce the memory consumption of the BufferQueue to the minimum possible without
+    // discarding data.
+    // The consumer invoking this method is responsible for calling getReleasedBuffers() after this
+    // call to free up any of its locally cached buffers.
     virtual status_t discardFreeBuffers() = 0;
 
     // dump state into a string
-    virtual void dump(String8& result, const char* prefix) const = 0;
+    virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0;
 
-public:
-    DECLARE_META_INTERFACE(GraphicBufferConsumer);
+    // Provide backwards source compatibility
+    void dumpState(String8& result, const char* prefix) {
+        String8 returned;
+        dumpState(String8(prefix), &returned);
+        result.append(returned);
+    }
 };
 
-// ----------------------------------------------------------------------------
-
-class BnGraphicBufferConsumer : public BnInterface<IGraphicBufferConsumer>
-{
+class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
 public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
+    BnGraphicBufferConsumer()
+          : SafeBnInterface<IGraphicBufferConsumer>("BnGraphicBufferConsumer") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
 };
 
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
+} // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index bf427fe..9250806 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -32,12 +32,17 @@
 
 #include <gui/FrameTimestamps.h>
 
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
 class IProducerListener;
 class NativeHandle;
 class Surface;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
 
 /*
  * This class defines the Binder IPC interface for the producer side of
@@ -56,7 +61,7 @@
 class IGraphicBufferProducer : public IInterface
 {
 public:
-    DECLARE_META_INTERFACE(GraphicBufferProducer);
+    DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer)
 
     enum {
         // A flag returned by dequeueBuffer when the client needs to call
@@ -190,7 +195,8 @@
     // 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, uint32_t usage) = 0;
+            uint32_t h, PixelFormat format, uint32_t usage,
+            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
@@ -294,7 +300,8 @@
 
     struct QueueBufferInput : public Flattenable<QueueBufferInput> {
         friend class Flattenable<QueueBufferInput>;
-        inline QueueBufferInput(const Parcel& parcel);
+        explicit inline QueueBufferInput(const Parcel& parcel);
+
         // timestamp - a monotonically increasing value in nanoseconds
         // isAutoTimestamp - if the timestamp was synthesized at queue time
         // dataSpace - description of the contents, interpretation depends on format
@@ -305,18 +312,23 @@
         //         set this to Fence::NO_FENCE if the buffer is ready immediately
         // sticky - the sticky transform set in Surface (only used by the LEGACY
         //          camera mode).
-        inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp,
-                android_dataspace dataSpace, const Rect& crop, int scalingMode,
-                uint32_t transform, const sp<Fence>& fence, uint32_t sticky = 0)
-                : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp),
-                  dataSpace(dataSpace), crop(crop), scalingMode(scalingMode),
-                  transform(transform), stickyTransform(sticky), fence(fence),
-                  surfaceDamage() { }
+        // getFrameTimestamps - whether or not the latest frame timestamps
+        //                      should be retrieved from the consumer.
+        inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
+                android_dataspace _dataSpace, const Rect& _crop,
+                int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
+                uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+                : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
+                  dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
+                  transform(_transform), stickyTransform(_sticky), fence(_fence),
+                  surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+
         inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
                 android_dataspace* outDataSpace,
                 Rect* outCrop, int* outScalingMode,
                 uint32_t* outTransform, sp<Fence>* outFence,
-                uint32_t* outStickyTransform = NULL) const {
+                uint32_t* outStickyTransform = nullptr,
+                bool* outGetFrameTimestamps = nullptr) const {
             *outTimestamp = timestamp;
             *outIsAutoTimestamp = bool(isAutoTimestamp);
             *outDataSpace = dataSpace;
@@ -327,9 +339,13 @@
             if (outStickyTransform != NULL) {
                 *outStickyTransform = stickyTransform;
             }
+            if (outGetFrameTimestamps) {
+                *outGetFrameTimestamps = getFrameTimestamps;
+            }
         }
 
         // Flattenable protocol
+        static constexpr size_t minFlattenedSize();
         size_t getFlattenedSize() const;
         size_t getFdCount() const;
         status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
@@ -339,51 +355,42 @@
         void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; }
 
     private:
-        int64_t timestamp;
-        int isAutoTimestamp;
-        android_dataspace dataSpace;
+        int64_t timestamp{0};
+        int isAutoTimestamp{0};
+        android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
         Rect crop;
-        int scalingMode;
-        uint32_t transform;
-        uint32_t stickyTransform;
+        int scalingMode{0};
+        uint32_t transform{0};
+        uint32_t stickyTransform{0};
         sp<Fence> fence;
         Region surfaceDamage;
+        bool getFrameTimestamps{false};
     };
 
-    // QueueBufferOutput must be a POD structure
-    struct __attribute__ ((__packed__)) QueueBufferOutput {
-        inline QueueBufferOutput() { }
-        // outWidth - filled with default width applied to the buffer
-        // outHeight - filled with default height applied to the buffer
-        // outTransformHint - filled with default transform applied to the buffer
-        // outNumPendingBuffers - num buffers queued that haven't yet been acquired
-        //                        (counting the currently queued buffer)
-        inline void deflate(uint32_t* outWidth,
-                uint32_t* outHeight,
-                uint32_t* outTransformHint,
-                uint32_t* outNumPendingBuffers,
-                uint64_t* outNextFrameNumber) const {
-            *outWidth = width;
-            *outHeight = height;
-            *outTransformHint = transformHint;
-            *outNumPendingBuffers = numPendingBuffers;
-            *outNextFrameNumber = nextFrameNumber;
-        }
-        inline void inflate(uint32_t inWidth, uint32_t inHeight,
-                uint32_t inTransformHint, uint32_t inNumPendingBuffers,
-                uint64_t inNextFrameNumber) {
-            width = inWidth;
-            height = inHeight;
-            transformHint = inTransformHint;
-            numPendingBuffers = inNumPendingBuffers;
-            nextFrameNumber = inNextFrameNumber;
-        }
-    private:
-        uint32_t width;
-        uint32_t height;
-        uint32_t transformHint;
-        uint32_t numPendingBuffers;
+    struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
+        QueueBufferOutput() = default;
+
+        // Moveable.
+        QueueBufferOutput(QueueBufferOutput&& src) = default;
+        QueueBufferOutput& operator=(QueueBufferOutput&& src) = default;
+        // Not copyable.
+        QueueBufferOutput(const QueueBufferOutput& src) = delete;
+        QueueBufferOutput& operator=(const QueueBufferOutput& src) = delete;
+
+        // Flattenable protocol
+        static constexpr size_t minFlattenedSize();
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+        uint32_t width{0};
+        uint32_t height{0};
+        uint32_t transformHint{0};
+        uint32_t numPendingBuffers{0};
         uint64_t nextFrameNumber{0};
+        FrameEventHistoryDelta frameTimestamps;
+        bool bufferReplaced{false};
     };
 
     virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
@@ -480,6 +487,7 @@
     // is considered a no-op.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the producer is not connected
     // * BAD_VALUE - one of the following has occurred:
     //             * the api specified does not match the one that was connected
     //             * api was out of range (see above).
@@ -580,13 +588,8 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
 
-    // Attempts to retrieve timestamp information for the given frame number.
-    // If information for the given frame number is not found, returns false.
-    // Returns true otherwise.
-    //
-    // If a fence has not yet signaled the timestamp returned will be 0;
-    virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
-            FrameTimestamps* /*outTimestamps*/) const { return false; }
+    // Gets the frame events that haven't already been retrieved.
+    virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
 
     // Returns a unique id for this BufferQueue
     virtual status_t getUniqueId(uint64_t* outId) const = 0;
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
index b7826c6..e808bd3 100644
--- a/include/gui/IProducerListener.h
+++ b/include/gui/IProducerListener.h
@@ -33,7 +33,7 @@
 {
 public:
     ProducerListener() {}
-    virtual ~ProducerListener() {}
+    virtual ~ProducerListener();
 
     // onBufferReleased is called from IGraphicBufferConsumer::releaseBuffer to
     // notify the producer that a new buffer is free and ready to be dequeued.
@@ -61,6 +61,7 @@
 class DummyProducerListener : public BnProducerListener
 {
 public:
+    virtual ~DummyProducerListener();
     virtual void onBufferReleased() {}
     virtual bool needsReleaseNotify() { return false; }
 };
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
deleted file mode 100644
index f64c6b8..0000000
--- a/include/gui/ISensorEventConnection.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class BitTube;
-
-class ISensorEventConnection : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(SensorEventConnection);
-
-    virtual sp<BitTube> getSensorChannel() const = 0;
-    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
-                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
-    virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
-    virtual status_t flush() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
deleted file mode 100644
index 571acb5..0000000
--- a/include/gui/ISensorServer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSORSERVER_H
-#define ANDROID_GUI_ISENSORSERVER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Sensor;
-class ISensorEventConnection;
-class String8;
-
-class ISensorServer : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(SensorServer);
-
-    virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
-    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
-
-    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
-             int mode, const String16& opPackageName) = 0;
-    virtual int32_t isDataInjectionEnabled() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorServer : public BnInterface<ISensorServer>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSORSERVER_H
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 74a4123..f80ba00 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -28,21 +28,23 @@
 #include <binder/IInterface.h>
 
 #include <ui/FrameStats.h>
+#include <ui/PixelFormat.h>
 
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposerClient.h>
+#include <vector>
 
 namespace android {
 // ----------------------------------------------------------------------------
 
-class ComposerState;
-class DisplayState;
+struct ComposerState;
+struct DisplayState;
 struct DisplayInfo;
 struct DisplayStatInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
-class IMemoryHeap;
+class IGraphicBufferProducer;
+class ISurfaceComposerClient;
 class Rect;
+enum class FrameEvent;
 
 /*
  * This class defines the Binder IPC interface for accessing various
@@ -50,7 +52,7 @@
  */
 class ISurfaceComposer: public IInterface {
 public:
-    DECLARE_META_INTERFACE(SurfaceComposer);
+    DECLARE_META_INTERFACE(SurfaceComposer)
 
     // flags for setTransactionState()
     enum {
@@ -70,17 +72,30 @@
         eRotate270  = 3
     };
 
+    enum VsyncSource {
+        eVsyncSourceApp = 0,
+        eVsyncSourceSurfaceFlinger = 1
+    };
+
     /* create connection with surface flinger, requires
      * ACCESS_SURFACE_FLINGER permission
      */
     virtual sp<ISurfaceComposerClient> createConnection() = 0;
 
-    /* create a graphic buffer allocator
+    /** create a scoped connection with surface flinger.
+     * Surfaces produced with this connection will act
+     * as children of the passed in GBP. That is to say
+     * SurfaceFlinger will draw them relative and confined to
+     * drawing of buffers from the layer associated with parent.
+     * As this is graphically equivalent in reach to just drawing
+     * pixels into the parent buffers, it requires no special permission.
      */
-    virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() = 0;
+    virtual sp<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& parent) = 0;
 
     /* return an IDisplayEventConnection */
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection() = 0;
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection(
+            VsyncSource vsyncSource = eVsyncSourceApp) = 0;
 
     /* create a virtual display
      * requires ACCESS_SURFACE_FLINGER permission.
@@ -112,6 +127,11 @@
     virtual bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& surface) const = 0;
 
+    /* Returns the frame timestamps supported by SurfaceFlinger.
+     */
+    virtual status_t getSupportedFrameTimestamps(
+            std::vector<FrameEvent>* outSupported) const = 0;
+
     /* set display power mode. depending on the mode, it can either trigger
      * screen on, off or low power mode and wait for it to complete.
      * requires ACCESS_SURFACE_FLINGER permission.
@@ -149,7 +169,7 @@
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform,
             Rotation rotation = eRotateNone) = 0;
 
@@ -171,6 +191,10 @@
      */
     virtual status_t getHdrCapabilities(const sp<IBinder>& display,
             HdrCapabilities* outCapabilities) const = 0;
+
+    virtual status_t enableVSyncInjections(bool enable) = 0;
+
+    virtual status_t injectVSync(nsecs_t when) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -182,13 +206,14 @@
         // Java by ActivityManagerService.
         BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
         CREATE_CONNECTION,
-        CREATE_GRAPHIC_BUFFER_ALLOC,
+        UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
         CREATE_DISPLAY_EVENT_CONNECTION,
         CREATE_DISPLAY,
         DESTROY_DISPLAY,
         GET_BUILT_IN_DISPLAY,
         SET_TRANSACTION_STATE,
         AUTHENTICATE_SURFACE,
+        GET_SUPPORTED_FRAME_TIMESTAMPS,
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
         SET_ACTIVE_CONFIG,
@@ -202,6 +227,9 @@
         GET_DISPLAY_COLOR_MODES,
         GET_ACTIVE_COLOR_MODE,
         SET_ACTIVE_COLOR_MODE,
+        ENABLE_VSYNC_INJECTIONS,
+        INJECT_VSYNC,
+        CREATE_SCOPED_CONNECTION
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h
index c27a741..2c613ea 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -14,54 +14,44 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
+#pragma once
 
 #include <binder/IInterface.h>
-
-#include <ui/FrameStats.h>
+#include <binder/SafeInterface.h>
 #include <ui/PixelFormat.h>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
+class FrameStats;
 class IGraphicBufferProducer;
 
-class ISurfaceComposerClient : public IInterface
-{
+class ISurfaceComposerClient : public IInterface {
 public:
-    DECLARE_META_INTERFACE(SurfaceComposerClient);
+    DECLARE_META_INTERFACE(SurfaceComposerClient)
 
     // flags for createSurface()
     enum { // (keep in sync with Surface.java)
-        eHidden             = 0x00000004,
-        eDestroyBackbuffer  = 0x00000020,
-        eSecure             = 0x00000080,
-        eNonPremultiplied   = 0x00000100,
-        eOpaque             = 0x00000400,
-        eProtectedByApp     = 0x00000800,
-        eProtectedByDRM     = 0x00001000,
-        eCursorWindow       = 0x00002000,
+        eHidden = 0x00000004,
+        eDestroyBackbuffer = 0x00000020,
+        eSecure = 0x00000080,
+        eNonPremultiplied = 0x00000100,
+        eOpaque = 0x00000400,
+        eProtectedByApp = 0x00000800,
+        eProtectedByDRM = 0x00001000,
+        eCursorWindow = 0x00002000,
 
-        eFXSurfaceNormal    = 0x00000000,
-        eFXSurfaceDim       = 0x00020000,
-        eFXSurfaceMask      = 0x000F0000,
+        eFXSurfaceNormal = 0x00000000,
+        eFXSurfaceDim = 0x00020000,
+        eFXSurfaceMask = 0x000F0000,
     };
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
      */
-    virtual status_t createSurface(
-            const String8& name, uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t flags,
-            sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp) = 0;
+    virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+                                   uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
+                                   uint32_t ownerUid, sp<IBinder>* handle,
+                                   sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -77,21 +67,14 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
-
-    virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
-            bool* outTransformToDisplayInverse) const = 0;
 };
 
-// ----------------------------------------------------------------------------
-
-class BnSurfaceComposerClient: public BnInterface<ISurfaceComposerClient> {
+class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
 public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-            Parcel* reply, uint32_t flags = 0);
+    BnSurfaceComposerClient()
+          : SafeBnInterface<ISurfaceComposerClient>("BnSurfaceComposerClient") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
 };
 
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/include/gui/OccupancyTracker.h b/include/gui/OccupancyTracker.h
index 1d15e7f..d4de8f2 100644
--- a/include/gui/OccupancyTracker.h
+++ b/include/gui/OccupancyTracker.h
@@ -45,12 +45,12 @@
             occupancyAverage(0.0f),
             usedThirdBuffer(false) {}
 
-        Segment(nsecs_t totalTime, size_t numFrames, float occupancyAverage,
-                bool usedThirdBuffer)
-          : totalTime(totalTime),
-            numFrames(numFrames),
-            occupancyAverage(occupancyAverage),
-            usedThirdBuffer(usedThirdBuffer) {}
+        Segment(nsecs_t _totalTime, size_t _numFrames, float _occupancyAverage,
+                bool _usedThirdBuffer)
+          : totalTime(_totalTime),
+            numFrames(_numFrames),
+            occupancyAverage(_occupancyAverage),
+            usedThirdBuffer(_usedThirdBuffer) {}
 
         // Parcelable interface
         virtual status_t writeToParcel(Parcel* parcel) const override;
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
deleted file mode 100644
index 094fd16..0000000
--- a/include/gui/Sensor.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_H
-#define ANDROID_GUI_SENSOR_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Flattenable.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include <hardware/sensors.h>
-
-#include <android/sensor.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensor { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Parcel;
-
-// ----------------------------------------------------------------------------
-
-class Sensor : public ASensor, public LightFlattenable<Sensor>
-{
-public:
-    enum {
-        TYPE_ACCELEROMETER  = ASENSOR_TYPE_ACCELEROMETER,
-        TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
-        TYPE_GYROSCOPE      = ASENSOR_TYPE_GYROSCOPE,
-        TYPE_LIGHT          = ASENSOR_TYPE_LIGHT,
-        TYPE_PROXIMITY      = ASENSOR_TYPE_PROXIMITY
-    };
-
-    struct uuid_t{
-        union {
-            uint8_t b[16];
-            int64_t i64[2];
-        };
-        uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
-        uuid_t() : b{0} {}
-    };
-
-    Sensor(const char * name = "");
-    Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
-    Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
-    ~Sensor();
-
-    const String8& getName() const;
-    const String8& getVendor() const;
-    int32_t getHandle() const;
-    int32_t getType() const;
-    float getMinValue() const;
-    float getMaxValue() const;
-    float getResolution() const;
-    float getPowerUsage() const;
-    int32_t getMinDelay() const;
-    nsecs_t getMinDelayNs() const;
-    int32_t getVersion() const;
-    uint32_t getFifoReservedEventCount() const;
-    uint32_t getFifoMaxEventCount() const;
-    const String8& getStringType() const;
-    const String8& getRequiredPermission() const;
-    bool isRequiredPermissionRuntime() const;
-    int32_t getRequiredAppOp() const;
-    int32_t getMaxDelay() const;
-    uint32_t getFlags() const;
-    bool isWakeUpSensor() const;
-    bool isDynamicSensor() const;
-    bool hasAdditionalInfo() const;
-    int32_t getReportingMode() const;
-
-    // Note that after setId() has been called, getUuid() no longer
-    // returns the UUID.
-    // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
-    //     make sure setId() doesn't change the UuidIndex.
-    const uuid_t& getUuid() const;
-    int32_t getId() const;
-    void setId(int32_t id);
-
-    // LightFlattenable protocol
-    inline bool isFixedSize() const { return false; }
-    size_t getFlattenedSize() const;
-    status_t flatten(void* buffer, size_t size) const;
-    status_t unflatten(void const* buffer, size_t size);
-
-private:
-    String8 mName;
-    String8 mVendor;
-    int32_t mHandle;
-    int32_t mType;
-    float   mMinValue;
-    float   mMaxValue;
-    float   mResolution;
-    float   mPower;
-    int32_t mMinDelay;
-    int32_t mVersion;
-    uint32_t mFifoReservedEventCount;
-    uint32_t mFifoMaxEventCount;
-    String8 mStringType;
-    String8 mRequiredPermission;
-    bool mRequiredPermissionRuntime = false;
-    int32_t mRequiredAppOp;
-    int32_t mMaxDelay;
-    uint32_t mFlags;
-    // TODO(b/29547335): Get rid of this field and replace with an index.
-    //     The index will be into a separate global vector of UUIDs.
-    //     Also add an mId field (and change flatten/unflatten appropriately).
-    uuid_t  mUuid;
-    static void flattenString8(void*& buffer, size_t& size, const String8& string8);
-    static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_H
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
deleted file mode 100644
index 4ee7c02..0000000
--- a/include/gui/SensorEventQueue.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SENSOR_EVENT_QUEUE_H
-#define ANDROID_SENSOR_EVENT_QUEUE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/String16.h>
-
-#include <gui/BitTube.h>
-
-// ----------------------------------------------------------------------------
-#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
-struct ALooper;
-struct ASensorEvent;
-
-// Concrete types for the NDK
-struct ASensorEventQueue {
-    ALooper* looper;
-};
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorEventConnection;
-class Sensor;
-class Looper;
-
-// ----------------------------------------------------------------------------
-
-class SensorEventQueue : public ASensorEventQueue, public RefBase
-{
-public:
-
-    enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
-
-    /**
-     * Typical sensor delay (sample period) in microseconds.
-     */
-    // Fastest sampling, system will bound it to minDelay
-    static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
-    // Typical sample period for game, 50Hz;
-    static constexpr int32_t SENSOR_DELAY_GAME = 20000;
-    // Typical sample period for UI, 15Hz
-    static constexpr int32_t SENSOR_DELAY_UI = 66667;
-    // Default sensor sample period
-    static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
-
-    SensorEventQueue(const sp<ISensorEventConnection>& connection);
-    virtual ~SensorEventQueue();
-    virtual void onFirstRef();
-
-    int getFd() const;
-
-    static ssize_t write(const sp<BitTube>& tube,
-            ASensorEvent const* events, size_t numEvents);
-
-    ssize_t read(ASensorEvent* events, size_t numEvents);
-
-    status_t waitForEvent() const;
-    status_t wake() const;
-
-    status_t enableSensor(Sensor const* sensor) const;
-    status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
-    status_t disableSensor(Sensor const* sensor) const;
-    status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
-
-    // these are here only to support SensorManager.java
-    status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int maxBatchReportLatencyUs,
-                          int reservedFlags) const;
-    status_t disableSensor(int32_t handle) const;
-    status_t flush() const;
-    // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
-    void sendAck(const ASensorEvent* events, int count);
-
-    status_t injectSensorEvent(const ASensorEvent& event);
-private:
-    sp<Looper> getLooper() const;
-    sp<ISensorEventConnection> mSensorEventConnection;
-    sp<BitTube> mSensorChannel;
-    mutable Mutex mLock;
-    mutable sp<Looper> mLooper;
-    ASensorEvent* mRecBuffer;
-    size_t mAvailable;
-    size_t mConsumed;
-    uint32_t mNumAcksToSend;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SENSOR_EVENT_QUEUE_H
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
deleted file mode 100644
index 6c6230f..0000000
--- a/include/gui/SensorManager.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_MANAGER_H
-#define ANDROID_GUI_SENSOR_MANAGER_H
-
-#include <map>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-#include <utils/Vector.h>
-#include <utils/String8.h>
-
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensorManager { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorServer;
-class Sensor;
-class SensorEventQueue;
-// ----------------------------------------------------------------------------
-
-class SensorManager :
-    public ASensorManager
-{
-public:
-    static SensorManager& getInstanceForPackage(const String16& packageName);
-    ~SensorManager();
-
-    ssize_t getSensorList(Sensor const* const** list);
-    ssize_t getDynamicSensorList(Vector<Sensor>& list);
-    Sensor const* getDefaultSensor(int type);
-    sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
-    bool isDataInjectionEnabled();
-
-private:
-    // DeathRecipient interface
-    void sensorManagerDied();
-
-    SensorManager(const String16& opPackageName);
-    status_t assertStateLocked();
-
-private:
-    static Mutex sLock;
-    static std::map<String16, SensorManager*> sPackageInstances;
-
-    Mutex mLock;
-    sp<ISensorServer> mSensorServer;
-    Sensor const** mSensorList;
-    Vector<Sensor> mSensors;
-    sp<IBinder::DeathRecipient> mDeathObserver;
-    const String16 mOpPackageName;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index f4a22cb..e8dc83e 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -18,21 +18,21 @@
 #define ANDROID_GUI_SURFACE_H
 
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
 
 #include <ui/ANativeObjectBase.h>
 #include <ui/Region.h>
 
-#include <binder/Parcelable.h>
-
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/KeyedVector.h>
 
 struct ANativeWindow_Buffer;
 
 namespace android {
 
+class ISurfaceComposer;
+
 /*
  * An implementation of ANativeWindow that feeds graphics buffers into a
  * BufferQueue.
@@ -66,7 +66,8 @@
      * the controlledByApp flag indicates that this Surface (producer) is
      * controlled by the application. This flag is used at connect time.
      */
-    Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
+    explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
+            bool controlledByApp = false);
 
     /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
      * Surface was created with. Usually it's an error to use the
@@ -134,17 +135,41 @@
     status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]);
 
+    status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration);
+
+    /* Enables or disables frame timestamp tracking. It is disabled by default
+     * to avoid overhead during queue and dequeue for applications that don't
+     * need the feature. If disabled, calls to getFrameTimestamps will fail.
+     */
+    void enableFrameTimestamps(bool enable);
+
+    status_t getCompositorTiming(
+            nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+            nsecs_t* compositeToPresentLatency);
+
     // See IGraphicBufferProducer::getFrameTimestamps
-    bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
-            nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
-            nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+    status_t getFrameTimestamps(uint64_t frameNumber,
+            nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+            nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+            nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
+            nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
             nsecs_t* outReleaseTime);
 
+    status_t getWideColorSupport(bool* supported);
+    status_t getHdrSupport(bool* supported);
+
     status_t getUniqueId(uint64_t* outId) const;
 
+    // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
+    nsecs_t getLastDequeueStartTime() const;
+
 protected:
     virtual ~Surface();
 
+    // Virtual for testing.
+    virtual sp<ISurfaceComposer> composerService() const;
+    virtual nsecs_t now() const;
+
 private:
     // can't be copied
     Surface& operator = (const Surface& rhs);
@@ -191,7 +216,13 @@
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
+    int dispatchGetDisplayRefreshCycleDuration(va_list args);
+    int dispatchGetNextFrameId(va_list args);
+    int dispatchEnableFrameTimestamps(va_list args);
+    int dispatchGetCompositorTiming(va_list args);
     int dispatchGetFrameTimestamps(va_list args);
+    int dispatchGetWideColorSupport(va_list args);
+    int dispatchGetHdrSupport(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -204,7 +235,6 @@
 
     virtual int connect(int api);
     virtual int setBufferCount(int bufferCount);
-    virtual int setBuffersDimensions(uint32_t width, uint32_t height);
     virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
     virtual int setBuffersFormat(PixelFormat format);
     virtual int setBuffersTransform(uint32_t transform);
@@ -224,20 +254,37 @@
     virtual int setAsyncMode(bool async);
     virtual int setSharedBufferMode(bool sharedBufferMode);
     virtual int setAutoRefresh(bool autoRefresh);
+    virtual int setBuffersDimensions(uint32_t width, uint32_t height);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
     virtual int query(int what, int* value) const;
 
     virtual int connect(int api, const sp<IProducerListener>& listener);
+
+    // When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch
+    // GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or
+    // attachBuffer call. This allows clients with their own buffer caches to free up buffers no
+    // longer in use by this surface.
+    virtual int connect(
+            int api, const sp<IProducerListener>& listener,
+            bool reportBufferRemoval);
     virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence);
     virtual int attachBuffer(ANativeWindowBuffer*);
 
+    // When client connects to Surface with reportBufferRemoval set to true, any buffers removed
+    // from this Surface will be collected and returned here. Once this method returns, these
+    // buffers will no longer be referenced by this Surface unless they are attached to this
+    // Surface later. The list of removed buffers will only be stored until the next dequeueBuffer,
+    // detachNextBuffer, or attachBuffer call.
+    status_t getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out);
+
 protected:
-    enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
+    enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
 
-private:
+    void querySupportedTimestampsLocked() const;
+
     void freeAllBuffers();
     int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
 
@@ -377,48 +424,26 @@
     nsecs_t mLastDequeueDuration = 0;
     nsecs_t mLastQueueDuration = 0;
 
+    // Stores the time right before we call IGBP::dequeueBuffer
+    nsecs_t mLastDequeueStartTime = 0;
+
     Condition mQueueBufferCondition;
 
-    uint64_t mNextFrameNumber;
+    uint64_t mNextFrameNumber = 1;
+    uint64_t mLastFrameNumber = 0;
+
+    // Mutable because ANativeWindow::query needs this class const.
+    mutable bool mQueriedSupportedTimestamps;
+    mutable bool mFrameTimestampsSupportsPresent;
+
+    // A cached copy of the FrameEventHistory maintained by the consumer.
+    bool mEnableFrameTimestamps = false;
+    std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
+
+    bool mReportRemovedBuffers = false;
+    std::vector<sp<GraphicBuffer>> mRemovedBuffers;
 };
 
-namespace view {
-
-/**
- * A simple holder for an IGraphicBufferProducer, to match the managed-side
- * android.view.Surface parcelable behavior.
- *
- * This implements android/view/Surface.aidl
- *
- * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
- * used in managed Binder calls.
- */
-class Surface : public Parcelable {
-  public:
-
-    String16 name;
-    sp<IGraphicBufferProducer> graphicBufferProducer;
-
-    virtual status_t writeToParcel(Parcel* parcel) const override;
-    virtual status_t readFromParcel(const Parcel* parcel) override;
-
-    // nameAlreadyWritten set to true by Surface.java, because it splits
-    // Parceling itself between managed and native code, so it only wants a part
-    // of the full parceling to happen on its native side.
-    status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
-
-    // nameAlreadyRead set to true by Surface.java, because it splits
-    // Parceling itself between managed and native code, so it only wants a part
-    // of the full parceling to happen on its native side.
-    status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
-
-  private:
-
-    static String16 readMaybeEmptyString16(const Parcel* parcel);
-};
-
-} // namespace view
-
-}; // namespace android
+} // namespace android
 
 #endif  // ANDROID_GUI_SURFACE_H
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index c4f88b6..ec310cf 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 
 #include <binder/IBinder.h>
-#include <binder/IMemory.h>
 
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -38,7 +37,7 @@
 
 // ---------------------------------------------------------------------------
 
-class DisplayInfo;
+struct DisplayInfo;
 class Composer;
 class HdrCapabilities;
 class ISurfaceComposerClient;
@@ -52,6 +51,7 @@
     friend class Composer;
 public:
                 SurfaceComposerClient();
+                SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
     virtual     ~SurfaceComposerClient();
 
     // Always make sure we could initialize
@@ -105,7 +105,10 @@
             uint32_t w,         // width in pixel
             uint32_t h,         // height in pixel
             PixelFormat format, // pixel-format desired
-            uint32_t flags = 0  // usage flags
+            uint32_t flags = 0, // usage flags
+            SurfaceControl* parent = nullptr, // parent
+            uint32_t windowType = 0, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
+            uint32_t ownerUid = 0 // UID of the task
     );
 
     //! Create a virtual display
@@ -131,6 +134,10 @@
     //! Close a composer transaction on all active SurfaceComposerClients.
     static void closeGlobalTransaction(bool synchronous = false);
 
+    static status_t enableVSyncInjections(bool enable);
+
+    static status_t injectVSync(nsecs_t when);
+
     //! Flag the currently open transaction as an animation transaction.
     static void setAnimationTransaction();
 
@@ -138,9 +145,11 @@
     status_t    show(const sp<IBinder>& id);
     status_t    setFlags(const sp<IBinder>& id, uint32_t flags, uint32_t mask);
     status_t    setTransparentRegionHint(const sp<IBinder>& id, const Region& transparent);
-    status_t    setLayer(const sp<IBinder>& id, uint32_t layer);
+    status_t    setLayer(const sp<IBinder>& id, int32_t layer);
+    status_t    setRelativeLayer(const sp<IBinder>& id,
+            const sp<IBinder>& relativeTo, int32_t layer);
     status_t    setAlpha(const sp<IBinder>& id, float alpha=1.0f);
-    status_t    setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dsdy, float dtdy);
+    status_t    setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dtdy, float dsdy);
     status_t    setPosition(const sp<IBinder>& id, float x, float y);
     status_t    setSize(const sp<IBinder>& id, uint32_t w, uint32_t h);
     status_t    setCrop(const sp<IBinder>& id, const Rect& crop);
@@ -148,6 +157,11 @@
     status_t    setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
     status_t    deferTransactionUntil(const sp<IBinder>& id,
             const sp<IBinder>& handle, uint64_t frameNumber);
+    status_t    deferTransactionUntil(const sp<IBinder>& id,
+            const sp<Surface>& handle, uint64_t frameNumber);
+    status_t    reparentChildren(const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
+    status_t    detachChildren(const sp<IBinder>& id);
     status_t    setOverrideScalingMode(const sp<IBinder>& id,
             int32_t overrideScalingMode);
     status_t    setGeometryAppliesWithResize(const sp<IBinder>& id);
@@ -157,9 +171,6 @@
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
     status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
 
-    status_t getTransformToDisplayInverse(const sp<IBinder>& token,
-            bool* outTransformToDisplayInverse) const;
-
     static status_t clearAnimationFrameStats();
     static status_t getAnimationFrameStats(FrameStats* outStats);
 
@@ -195,6 +206,7 @@
                 status_t                    mStatus;
                 sp<ISurfaceComposerClient>  mClient;
                 Composer&                   mComposer;
+                wp<IGraphicBufferProducer>  mParent;
 };
 
 // ---------------------------------------------------------------------------
@@ -208,9 +220,15 @@
             const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform);
-
+    static status_t captureToBuffer(
+            const sp<IBinder>& display,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+            int32_t minLayerZ, int32_t maxLayerZ,
+            bool useIdentityTransform,
+            uint32_t rotation,
+            sp<GraphicBuffer>* outbuffer);
 private:
     mutable sp<CpuConsumer> mCpuConsumer;
     mutable sp<IGraphicBufferProducer> mProducer;
@@ -231,11 +249,11 @@
             bool useIdentityTransform);
     status_t update(const sp<IBinder>& display,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform);
     status_t update(const sp<IBinder>& display,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform, uint32_t rotation);
 
     sp<CpuConsumer> getCpuConsumer() const;
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index 5e731c3..8bb705c 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -61,7 +61,28 @@
     void        disconnect();
 
     status_t    setLayerStack(uint32_t layerStack);
-    status_t    setLayer(uint32_t layer);
+    status_t    setLayer(int32_t layer);
+
+    // Sets a Z order relative to the Surface specified by "relativeTo" but
+    // without becoming a full child of the relative. Z-ordering works exactly
+    // as if it were a child however.
+    //
+    // As a nod to sanity, only non-child surfaces may have a relative Z-order.
+    //
+    // This overrides any previous and is overriden by any future calls
+    // to setLayer.
+    //
+    // If the relative dissapears, the Surface will have no layer and be
+    // invisible, until the next time set(Relative)Layer is called.
+    //
+    // TODO: This is probably a hack. Currently it exists only to work around
+    // some framework usage of the hidden APPLICATION_MEDIA_OVERLAY window type
+    // which allows inserting a window between a SurfaceView and it's main application
+    // window. However, since we are using child windows for the SurfaceView, but not using
+    // child windows elsewhere in O, the WindowManager can't set the layer appropriately.
+    // This is only used by the "TvInputService" and following the port of ViewRootImpl
+    // to child surfaces, we can then port this and remove this method.
+    status_t    setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer);
     status_t    setPosition(float x, float y);
     status_t    setSize(uint32_t w, uint32_t h);
     status_t    hide();
@@ -69,7 +90,7 @@
     status_t    setFlags(uint32_t flags, uint32_t mask);
     status_t    setTransparentRegionHint(const Region& transparent);
     status_t    setAlpha(float alpha=1.0f);
-    status_t    setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
+    status_t    setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
     status_t    setCrop(const Rect& crop);
     status_t    setFinalCrop(const Rect& crop);
 
@@ -80,8 +101,30 @@
     status_t    setGeometryAppliesWithResize();
 
     // Defers applying any changes made in this transaction until the Layer
-    // identified by handle reaches the given frameNumber
-    status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
+    // identified by handle reaches the given frameNumber. If the Layer identified
+    // by handle is removed, then we will apply this transaction regardless of
+    // what frame number has been reached.
+    status_t deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+
+    // A variant of deferTransactionUntil which identifies the Layer we wait for by
+    // Surface instead of Handle. Useful for clients which may not have the
+    // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
+    status_t deferTransactionUntil(const sp<Surface>& barrier, uint64_t frameNumber);
+
+    // Reparents all children of this layer to the new parent handle.
+    status_t reparentChildren(const sp<IBinder>& newParentHandle);
+
+    // Detaches all child surfaces (and their children recursively)
+    // from their SurfaceControl.
+    // The child SurfaceControl's will not throw exceptions or return errors,
+    // but transactions will have no effect.
+    // The child surfaces will continue to follow their parent surfaces,
+    // and remain eligible for rendering, but their relative state will be
+    // frozen. We use this in the WindowManager, in app shutdown/relaunch
+    // scenarios, where the app would otherwise clean up its child Surfaces.
+    // Sometimes the WindowManager needs to extend their lifetime slightly
+    // in order to perform an exit animation or prevent flicker.
+    status_t detachChildren();
 
     // Set an override scaling mode as documented in <system/window.h>
     // the override scaling mode will take precedence over any client
@@ -92,13 +135,12 @@
             const sp<SurfaceControl>& control, Parcel* parcel);
 
     sp<Surface> getSurface() const;
+    sp<Surface> createSurface() const;
     sp<IBinder> getHandle() const;
 
     status_t clearLayerFrameStats() const;
     status_t getLayerFrameStats(FrameStats* outStats) const;
 
-    status_t getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const;
-
 private:
     // can't be copied
     SurfaceControl& operator = (SurfaceControl& rhs);
@@ -114,6 +156,7 @@
 
     ~SurfaceControl();
 
+    sp<Surface> generateSurfaceLocked() const;
     status_t validate() const;
     void destroy();
 
diff --git a/include/gui/bufferqueue/1.0/B2HProducerListener.h b/include/gui/bufferqueue/1.0/B2HProducerListener.h
new file mode 100644
index 0000000..fa6c2d9
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/B2HProducerListener.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IProducerListener
+        BProducerListener;
+
+struct B2HProducerListener : public HProducerListener {
+    sp<BProducerListener> mBase;
+    B2HProducerListener(sp<BProducerListener> const& base);
+    Return<void> onBufferReleased() override;
+    Return<bool> needsReleaseNotify() override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
diff --git a/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
new file mode 100644
index 0000000..93c452a
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::media::V1_0::AnwBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+using ::android::BnGraphicBufferProducer;
+using ::android::IProducerListener;
+
+struct H2BGraphicBufferProducer : public ::android::H2BConverter<
+        HGraphicBufferProducer,
+        BGraphicBufferProducer,
+        BnGraphicBufferProducer> {
+    H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {}
+
+    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, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override;
+    status_t detachBuffer(int slot) override;
+    status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence)
+            override;
+    status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer)
+            override;
+    status_t queueBuffer(int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override;
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+    int query(int what, int* value) override;
+    status_t connect(const sp<IProducerListener>& listener, int api,
+            bool producerControlledByApp, QueueBufferOutput* output) override;
+    status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api)
+            override;
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+    void allocateBuffers(uint32_t width, uint32_t height,
+            ::android::PixelFormat format, uint32_t usage) override;
+    status_t allowAllocation(bool allow) override;
+    status_t setGenerationNumber(uint32_t generationNumber) override;
+    String8 getConsumerName() const override;
+    status_t setSharedBufferMode(bool sharedBufferMode) override;
+    status_t setAutoRefresh(bool autoRefresh) override;
+    status_t setDequeueTimeout(nsecs_t timeout) override;
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+          sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
+    status_t getUniqueId(uint64_t* outId) const override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/include/gui/view/Surface.h b/include/gui/view/Surface.h
new file mode 100644
index 0000000..cc64fd4
--- /dev/null
+++ b/include/gui/view/Surface.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_VIEW_SURFACE_H
+#define ANDROID_GUI_VIEW_SURFACE_H
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/String16.h>
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+class IGraphicBufferProducer;
+
+namespace view {
+
+/**
+ * A simple holder for an IGraphicBufferProducer, to match the managed-side
+ * android.view.Surface parcelable behavior.
+ *
+ * This implements android/view/Surface.aidl
+ *
+ * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
+ * used in managed Binder calls.
+ */
+class Surface : public Parcelable {
+  public:
+
+    String16 name;
+    sp<IGraphicBufferProducer> graphicBufferProducer;
+
+    virtual status_t writeToParcel(Parcel* parcel) const override;
+    virtual status_t readFromParcel(const Parcel* parcel) override;
+
+    // nameAlreadyWritten set to true by Surface.java, because it splits
+    // Parceling itself between managed and native code, so it only wants a part
+    // of the full parceling to happen on its native side.
+    status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
+
+    // nameAlreadyRead set to true by Surface.java, because it splits
+    // Parceling itself between managed and native code, so it only wants a part
+    // of the full parceling to happen on its native side.
+    status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
+
+  private:
+
+    static String16 readMaybeEmptyString16(const Parcel* parcel);
+};
+
+} // namespace view
+} // namespace android
+
+#endif  // ANDROID_GUI_VIEW_SURFACE_H
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
new file mode 100644
index 0000000..86da4d3
--- /dev/null
+++ b/include/input/DisplayViewport.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
+#define _LIBINPUT_DISPLAY_VIEWPORT_H
+
+#include <ui/DisplayInfo.h>
+#include <input/Input.h>
+
+namespace android {
+
+/*
+ * Describes how coordinates are mapped on a physical display.
+ * See com.android.server.display.DisplayViewport.
+ */
+struct DisplayViewport {
+    int32_t displayId; // -1 if invalid
+    int32_t orientation;
+    int32_t logicalLeft;
+    int32_t logicalTop;
+    int32_t logicalRight;
+    int32_t logicalBottom;
+    int32_t physicalLeft;
+    int32_t physicalTop;
+    int32_t physicalRight;
+    int32_t physicalBottom;
+    int32_t deviceWidth;
+    int32_t deviceHeight;
+    String8 uniqueId;
+
+    DisplayViewport() :
+            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
+            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
+            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
+            deviceWidth(0), deviceHeight(0) {
+    }
+
+    bool operator==(const DisplayViewport& other) const {
+        return displayId == other.displayId
+                && orientation == other.orientation
+                && logicalLeft == other.logicalLeft
+                && logicalTop == other.logicalTop
+                && logicalRight == other.logicalRight
+                && logicalBottom == other.logicalBottom
+                && physicalLeft == other.physicalLeft
+                && physicalTop == other.physicalTop
+                && physicalRight == other.physicalRight
+                && physicalBottom == other.physicalBottom
+                && deviceWidth == other.deviceWidth
+                && deviceHeight == other.deviceHeight
+                && uniqueId == other.uniqueId;
+    }
+
+    bool operator!=(const DisplayViewport& other) const {
+        return !(*this == other);
+    }
+
+    inline bool isValid() const {
+        return displayId >= 0;
+    }
+
+    void setNonDisplayViewport(int32_t width, int32_t height) {
+        displayId = ADISPLAY_ID_NONE;
+        orientation = DISPLAY_ORIENTATION_0;
+        logicalLeft = 0;
+        logicalTop = 0;
+        logicalRight = width;
+        logicalBottom = height;
+        physicalLeft = 0;
+        physicalTop = 0;
+        physicalRight = width;
+        physicalBottom = height;
+        deviceWidth = width;
+        deviceHeight = height;
+        uniqueId.clear();
+    }
+};
+
+/**
+ * Describes the different type of viewports supported by input flinger.
+ * Keep in sync with values in InputManagerService.java.
+ */
+enum class ViewportType : int32_t {
+    VIEWPORT_INTERNAL = 1,
+    VIEWPORT_EXTERNAL = 2,
+    VIEWPORT_VIRTUAL = 3,
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_DISPLAY_VIEWPORT_H
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 629310f..11bb721 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -30,7 +30,7 @@
  */
 class IInputFlinger : public IInterface {
 public:
-    DECLARE_META_INTERFACE(InputFlinger);
+    DECLARE_META_INTERFACE(InputFlinger)
 };
 
 
diff --git a/include/input/Input.h b/include/input/Input.h
index 55787e7..cfcafab 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -398,7 +398,7 @@
 
     inline int32_t getButtonState() const { return mButtonState; }
 
-    inline int32_t setButtonState(int32_t buttonState) { mButtonState = buttonState; }
+    inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; }
 
     inline int32_t getActionButton() const { return mActionButton; }
 
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 0bd14ea..20154eb 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -425,30 +425,30 @@
     return NULL;
 }
 
-static int32_t getKeyCodeByLabel(const char* label) {
+static inline int32_t getKeyCodeByLabel(const char* label) {
     return int32_t(lookupValueByLabel(label, KEYCODES));
 }
 
-static const char* getLabelByKeyCode(int32_t keyCode) {
-    if (keyCode >= 0 && keyCode < size(KEYCODES)) {
+static inline const char* getLabelByKeyCode(int32_t keyCode) {
+    if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
         return KEYCODES[keyCode].literal;
     }
     return NULL;
 }
 
-static uint32_t getKeyFlagByLabel(const char* label) {
+static inline uint32_t getKeyFlagByLabel(const char* label) {
     return uint32_t(lookupValueByLabel(label, FLAGS));
 }
 
-static int32_t getAxisByLabel(const char* label) {
+static inline int32_t getAxisByLabel(const char* label) {
     return int32_t(lookupValueByLabel(label, AXES));
 }
 
-static const char* getAxisLabel(int32_t axisId) {
+static inline const char* getAxisLabel(int32_t axisId) {
     return lookupLabelByValue(axisId, AXES);
 }
 
-static int32_t getLedByLabel(const char* label) {
+static inline int32_t getLedByLabel(const char* label) {
     return int32_t(lookupValueByLabel(label, LEDS));
 }
 
diff --git a/include/media/cas/CasAPI.h b/include/media/cas/CasAPI.h
new file mode 100644
index 0000000..67f4511
--- /dev/null
+++ b/include/media/cas/CasAPI.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAS_API_H_
+#define CAS_API_H_
+
+#include <vector>
+#include <utils/String8.h>
+
+//  Loadable CasPlugin shared libraries should define the entry points
+//  as shown below:
+//
+//  extern "C" {
+//      extern android::CasFactory *createCasFactory();
+//      extern android::DescramblerFactory *createDescramblerFactory();
+//  }
+
+namespace android {
+
+struct CasPlugin;
+
+struct CasPluginDescriptor {
+    int32_t CA_system_id;
+    String8 name;
+};
+
+typedef std::vector<uint8_t> CasData;
+typedef std::vector<uint8_t> CasSessionId;
+typedef std::vector<uint8_t> CasEmm;
+typedef std::vector<uint8_t> CasEcm;
+typedef void (*CasPluginCallback)(
+        void *appData,
+        int32_t event,
+        int32_t arg,
+        uint8_t *data,
+        size_t size);
+
+struct CasFactory {
+    CasFactory() {}
+    virtual ~CasFactory() {}
+
+    // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+    virtual bool isSystemIdSupported(
+            int32_t CA_system_id) const = 0;
+
+    // Get a list of the CA schemes supported by the plugin.
+    virtual status_t queryPlugins(
+            std::vector<CasPluginDescriptor> *descriptors) const = 0;
+
+    // Construct a new instance of a CasPlugin given a CA_system_id
+    virtual status_t createPlugin(
+            int32_t CA_system_id,
+            uint64_t appData,
+            CasPluginCallback callback,
+            CasPlugin **plugin) = 0;
+
+private:
+    CasFactory(const CasFactory &);
+    CasFactory &operator=(const CasFactory &); /* NOLINT */
+};
+
+struct CasPlugin {
+    CasPlugin() {}
+    virtual ~CasPlugin() {}
+
+    // Provide the CA private data from a CA_descriptor in the conditional
+    // access table to a CasPlugin.
+    virtual status_t setPrivateData(
+            const CasData &privateData) = 0;
+
+    // Open a session for descrambling a program, or one or more elementary
+    // streams.
+    virtual status_t openSession(CasSessionId *sessionId) = 0;
+
+    // Close a previously opened session.
+    virtual status_t closeSession(const CasSessionId &sessionId) = 0;
+
+    // Provide the CA private data from a CA_descriptor in the program map
+    // table to a CasPlugin.
+    virtual status_t setSessionPrivateData(
+            const CasSessionId &sessionId,
+            const CasData &privateData) = 0;
+
+    // Process an ECM from the ECM stream for this session’s elementary stream.
+    virtual status_t processEcm(
+            const CasSessionId &sessionId,
+            const CasEcm &ecm) = 0;
+
+    // Process an in-band EMM from the EMM stream.
+    virtual status_t processEmm(
+            const CasEmm &emm) = 0;
+
+    // Deliver an event to the CasPlugin. The format of the event is specific
+    // to the CA scheme and is opaque to the framework.
+    virtual status_t sendEvent(
+            int32_t event,
+            int32_t arg,
+            const CasData &eventData) = 0;
+
+    // Native implementation of the MediaCas Java API provision method.
+    virtual status_t provision(
+            const String8 &provisionString) = 0;
+
+    // Native implementation of the MediaCas Java API refreshEntitlements method
+    virtual status_t refreshEntitlements(
+            int32_t refreshType,
+            const CasData &refreshData) = 0;
+
+private:
+    CasPlugin(const CasPlugin &);
+    CasPlugin &operator=(const CasPlugin &); /* NOLINT */
+};
+
+extern "C" {
+    extern android::CasFactory *createCasFactory();
+}
+
+} // namespace android
+
+#endif // CAS_API_H_
diff --git a/include/media/cas/DescramblerAPI.h b/include/media/cas/DescramblerAPI.h
new file mode 100644
index 0000000..0a51952
--- /dev/null
+++ b/include/media/cas/DescramblerAPI.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DESCRAMBLER_API_H_
+#define DESCRAMBLER_API_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/cas/CasAPI.h>
+
+namespace android {
+
+struct AString;
+struct DescramblerPlugin;
+
+struct DescramblerFactory {
+    DescramblerFactory() {}
+    virtual ~DescramblerFactory() {}
+
+    // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+    virtual bool isSystemIdSupported(
+            int32_t CA_system_id) const = 0;
+
+    // Construct a new instance of a DescramblerPlugin given a CA_system_id
+    virtual status_t createPlugin(
+            int32_t CA_system_id, DescramblerPlugin **plugin) = 0;
+
+private:
+    DescramblerFactory(const DescramblerFactory &);
+    DescramblerFactory &operator=(const DescramblerFactory &);
+};
+
+struct DescramblerPlugin {
+    enum ScramblingControl {
+        kScrambling_Unscrambled = 0,
+        kScrambling_Reserved    = 1,
+        kScrambling_EvenKey     = 2,
+        kScrambling_OddKey      = 3,
+    };
+
+    struct SubSample {
+        uint32_t mNumBytesOfClearData;
+        uint32_t mNumBytesOfEncryptedData;
+    };
+
+    DescramblerPlugin() {}
+    virtual ~DescramblerPlugin() {}
+
+    // If this method returns false, a non-secure decoder will be used to
+    // decode the data after decryption. The decrypt API below will have
+    // to support insecure decryption of the data (secure = false) for
+    // media data of the given mime type.
+    virtual bool requiresSecureDecoderComponent(const char *mime) const = 0;
+
+    // A MediaCas session may be associated with a MediaCrypto session.  The
+    // associated MediaCas session is used to load decryption keys
+    // into the crypto/cas plugin.  The keys are then referenced by key-id
+    // in the 'key' parameter to the decrypt() method.
+    // Should return NO_ERROR on success, ERROR_DRM_SESSION_NOT_OPENED if
+    // the session is not opened and a code from MediaErrors.h otherwise.
+    virtual status_t setMediaCasSession(const CasSessionId& sessionId) = 0;
+
+    // If the error returned falls into the range
+    // ERROR_DRM_VENDOR_MIN..ERROR_DRM_VENDOR_MAX, errorDetailMsg should be
+    // filled in with an appropriate string.
+    // At the java level these special errors will then trigger a
+    // MediaCodec.CryptoException that gives clients access to both
+    // the error code and the errorDetailMsg.
+    // Returns a non-negative result to indicate the number of bytes written
+    // to the dstPtr, or a negative result to indicate an error.
+    virtual ssize_t descramble(
+            bool secure,
+            ScramblingControl scramblingControl,
+            size_t numSubSamples,
+            const SubSample *subSamples,
+            const void *srcPtr,
+            int32_t srcOffset,
+            void *dstPtr,
+            int32_t dstOffset,
+            AString *errorDetailMsg) = 0;
+
+private:
+    DescramblerPlugin(const DescramblerPlugin &);
+    DescramblerPlugin &operator=(const DescramblerPlugin &);
+};
+
+}  // namespace android
+
+extern "C" {
+    extern android::DescramblerFactory *createDescramblerFactory();
+}
+
+#endif  // DESCRAMBLER_API_H_
diff --git a/include/media/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h
index 3a53e9f..30cd5fd 100644
--- a/include/media/hardware/HDCPAPI.h
+++ b/include/media/hardware/HDCPAPI.h
@@ -73,7 +73,7 @@
     // Module can call the notification function to signal completion/failure
     // of asynchronous operations (such as initialization) or out of band
     // events.
-    HDCPModule(void *cookie, ObserverFunc observerNotify) {};
+    HDCPModule(void * /*cookie*/, ObserverFunc /*observerNotify*/) {};
 
     virtual ~HDCPModule() {};
 
@@ -104,8 +104,8 @@
     // 1 for the second and so on)
     // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
     virtual status_t encrypt(
-            const void *inData, size_t size, uint32_t streamCTR,
-            uint64_t *outInputCTR, void *outData) {
+            const void * /*inData*/, size_t /*size*/, uint32_t /*streamCTR*/,
+            uint64_t * /*outInputCTR*/, void * /*outData*/) {
         return INVALID_OPERATION;
     }
 
@@ -119,8 +119,8 @@
     // 1 for the second and so on)
     // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
     virtual status_t encryptNative(
-            buffer_handle_t buffer, size_t offset, size_t size,
-            uint32_t streamCTR, uint64_t *outInputCTR, void *outData) {
+            buffer_handle_t /*buffer*/, size_t /*offset*/, size_t /*size*/,
+            uint32_t /*streamCTR*/, uint64_t * /*outInputCTR*/, void * /*outData*/) {
         return INVALID_OPERATION;
     }
     // DECRYPTION only:
@@ -133,9 +133,9 @@
     // until outData contains size bytes of decrypted data.
     // Both streamCTR and inputCTR will be provided by the caller.
     virtual status_t decrypt(
-            const void *inData, size_t size,
-            uint32_t streamCTR, uint64_t inputCTR,
-            void *outData) {
+            const void * /*inData*/, size_t /*size*/,
+            uint32_t /*streamCTR*/, uint64_t /*inputCTR*/,
+            void * /*outData*/) {
         return INVALID_OPERATION;
     }
 
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index 2c50ad6..cecf715 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -270,7 +270,7 @@
     // output: fill out the MediaImage fields
     MediaImage sMediaImage;
 
-    DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only
+    explicit DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only
 };
 
 // A pointer to this struct is passed to OMX_GetParameter when the extension
diff --git a/include/media/hardware/VideoAPI.h b/include/media/hardware/VideoAPI.h
index 3667c4b..a090876 100644
--- a/include/media/hardware/VideoAPI.h
+++ b/include/media/hardware/VideoAPI.h
@@ -110,7 +110,7 @@
 // though could verify that nSize is at least the size of the structure at the
 // time of implementation. All new fields will be added at the end of the structure
 // ensuring backward compatibility.
-struct __attribute__ ((__packed__)) ColorAspects {
+struct __attribute__ ((__packed__, aligned(alignof(uint32_t)))) ColorAspects {
     // this is in sync with the range values in graphics.h
     enum Range : uint32_t {
         RangeUnspecified,
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..56d7cc8 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -107,6 +107,7 @@
         case OMX_AUDIO_AACObjectLTP:      return "LTP";
         case OMX_AUDIO_AACObjectHE:       return "HE";
         case OMX_AUDIO_AACObjectScalable: return "Scalable";
+        case OMX_AUDIO_AACObjectER_Scalable: return "ER_Scalable";
         case OMX_AUDIO_AACObjectERLC:     return "ERLC";
         case OMX_AUDIO_AACObjectLD:       return "LD";
         case OMX_AUDIO_AACObjectHE_PS:    return "HE_PS";
@@ -530,9 +531,12 @@
 //      case OMX_IndexConfigCallbackRequest:            return "ConfigCallbackRequest";
 //      case OMX_IndexConfigCommitMode:                 return "ConfigCommitMode";
 //      case OMX_IndexConfigCommit:                     return "ConfigCommit";
+        case OMX_IndexConfigAndroidVendorExtension:     return "ConfigAndroidVendorExtension";
         case OMX_IndexParamAudioAndroidAc3:             return "ParamAudioAndroidAc3";
         case OMX_IndexParamAudioAndroidOpus:            return "ParamAudioAndroidOpus";
         case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
+        case OMX_IndexParamAudioAndroidEac3:            return "ParamAudioAndroidEac3";
+        case OMX_IndexParamAudioProfileQuerySupported:  return "ParamAudioProfileQuerySupported";
 //      case OMX_IndexParamNalStreamFormatSupported:    return "ParamNalStreamFormatSupported";
 //      case OMX_IndexParamNalStreamFormat:             return "ParamNalStreamFormat";
 //      case OMX_IndexParamNalStreamFormatSelect:       return "ParamNalStreamFormatSelect";
@@ -545,10 +549,15 @@
         case OMX_IndexConfigAndroidIntraRefresh:        return "ConfigAndroidIntraRefresh";
         case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
         case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
+        case OMX_IndexParamMaxFrameDurationForBitrateControl:
+            return "ParamMaxFrameDurationForBitrateControl";
+        case OMX_IndexParamVideoVp9:                    return "ParamVideoVp9";
+        case OMX_IndexParamVideoAndroidVp9Encoder:      return "ParamVideoAndroidVp9Encoder";
         case OMX_IndexConfigAutoFramerateConversion:    return "ConfigAutoFramerateConversion";
         case OMX_IndexConfigPriority:                   return "ConfigPriority";
         case OMX_IndexConfigOperatingRate:              return "ConfigOperatingRate";
         case OMX_IndexParamConsumerUsageBits:           return "ParamConsumerUsageBits";
+        case OMX_IndexConfigLatency:                    return "ConfigLatency";
         default:                                        return asString((OMX_INDEXTYPE)i, def);
     }
 }
diff --git a/include/media/openmax/OMX_Audio.h b/include/media/openmax/OMX_Audio.h
index d8bee76..9c0296b 100644
--- a/include/media/openmax/OMX_Audio.h
+++ b/include/media/openmax/OMX_Audio.h
@@ -259,6 +259,7 @@
   OMX_AUDIO_AACObjectHE,            /**< AAC High Efficiency (object type SBR, HE-AAC profile) */
   OMX_AUDIO_AACObjectScalable,      /**< AAC Scalable object */
   OMX_AUDIO_AACObjectERLC = 17,     /**< ER AAC Low Complexity object (Error Resilient AAC-LC) */
+  OMX_AUDIO_AACObjectER_Scalable = 20, /**< ER AAC scalable object */
   OMX_AUDIO_AACObjectLD = 23,       /**< AAC Low Delay object (Error Resilient) */
   OMX_AUDIO_AACObjectHE_PS = 29,    /**< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) */
   OMX_AUDIO_AACObjectELD = 39,      /** AAC Enhanced Low Delay. NOTE: Pending Khronos standardization **/
diff --git a/include/media/openmax/OMX_Core.h b/include/media/openmax/OMX_Core.h
index 88dd585..bb974b3 100644
--- a/include/media/openmax/OMX_Core.h
+++ b/include/media/openmax/OMX_Core.h
@@ -738,7 +738,7 @@
         pComponentVersion,                                  \
         pSpecVersion,                                       \
         pComponentUUID)                                     \
-    ((OMX_COMPONENTTYPE*)hComponent)->GetComponentVersion(  \
+    ((OMX_COMPONENTTYPE*)(hComponent))->GetComponentVersion(\
         hComponent,                                         \
         pComponentName,                                     \
         pComponentVersion,                                  \
@@ -804,7 +804,7 @@
          Cmd,                                               \
          nParam,                                            \
          pCmdData)                                          \
-     ((OMX_COMPONENTTYPE*)hComponent)->SendCommand(         \
+     ((OMX_COMPONENTTYPE*)(hComponent))->SendCommand(       \
          hComponent,                                        \
          Cmd,                                               \
          nParam,                                            \
@@ -843,8 +843,8 @@
 #define OMX_GetParameter(                                   \
         hComponent,                                         \
         nParamIndex,                                        \
-        pComponentParameterStructure)                        \
-    ((OMX_COMPONENTTYPE*)hComponent)->GetParameter(         \
+        pComponentParameterStructure)                       \
+    ((OMX_COMPONENTTYPE*)(hComponent))->GetParameter(       \
         hComponent,                                         \
         nParamIndex,                                        \
         pComponentParameterStructure)    /* Macro End */
@@ -882,8 +882,8 @@
 #define OMX_SetParameter(                                   \
         hComponent,                                         \
         nParamIndex,                                        \
-        pComponentParameterStructure)                        \
-    ((OMX_COMPONENTTYPE*)hComponent)->SetParameter(         \
+        pComponentParameterStructure)                       \
+    ((OMX_COMPONENTTYPE*)(hComponent))->SetParameter(       \
         hComponent,                                         \
         nParamIndex,                                        \
         pComponentParameterStructure)    /* Macro End */
@@ -918,8 +918,8 @@
 #define OMX_GetConfig(                                      \
         hComponent,                                         \
         nConfigIndex,                                       \
-        pComponentConfigStructure)                           \
-    ((OMX_COMPONENTTYPE*)hComponent)->GetConfig(            \
+        pComponentConfigStructure)                          \
+    ((OMX_COMPONENTTYPE*)(hComponent))->GetConfig(          \
         hComponent,                                         \
         nConfigIndex,                                       \
         pComponentConfigStructure)       /* Macro End */
@@ -954,8 +954,8 @@
 #define OMX_SetConfig(                                      \
         hComponent,                                         \
         nConfigIndex,                                       \
-        pComponentConfigStructure)                           \
-    ((OMX_COMPONENTTYPE*)hComponent)->SetConfig(            \
+        pComponentConfigStructure)                          \
+    ((OMX_COMPONENTTYPE*)(hComponent))->SetConfig(          \
         hComponent,                                         \
         nConfigIndex,                                       \
         pComponentConfigStructure)       /* Macro End */
@@ -989,7 +989,7 @@
         hComponent,                                         \
         cParameterName,                                     \
         pIndexType)                                         \
-    ((OMX_COMPONENTTYPE*)hComponent)->GetExtensionIndex(    \
+    ((OMX_COMPONENTTYPE*)(hComponent))->GetExtensionIndex(  \
         hComponent,                                         \
         cParameterName,                                     \
         pIndexType)                     /* Macro End */
@@ -1015,7 +1015,7 @@
 #define OMX_GetState(                                       \
         hComponent,                                         \
         pState)                                             \
-    ((OMX_COMPONENTTYPE*)hComponent)->GetState(             \
+    ((OMX_COMPONENTTYPE*)(hComponent))->GetState(           \
         hComponent,                                         \
         pState)                         /* Macro End */
 
@@ -1046,7 +1046,7 @@
            pAppPrivate,                                     \
            nSizeBytes,                                      \
            pBuffer)                                         \
-    ((OMX_COMPONENTTYPE*)hComponent)->UseBuffer(            \
+    ((OMX_COMPONENTTYPE*)(hComponent))->UseBuffer(          \
            hComponent,                                      \
            ppBufferHdr,                                     \
            nPortIndex,                                      \
@@ -1088,7 +1088,7 @@
         nPortIndex,                                         \
         pAppPrivate,                                        \
         nSizeBytes)                                         \
-    ((OMX_COMPONENTTYPE*)hComponent)->AllocateBuffer(       \
+    ((OMX_COMPONENTTYPE*)(hComponent))->AllocateBuffer(     \
         hComponent,                                         \
         ppBuffer,                                           \
         nPortIndex,                                         \
@@ -1122,7 +1122,7 @@
         hComponent,                                         \
         nPortIndex,                                         \
         pBuffer)                                            \
-    ((OMX_COMPONENTTYPE*)hComponent)->FreeBuffer(           \
+    ((OMX_COMPONENTTYPE*)(hComponent))->FreeBuffer(         \
         hComponent,                                         \
         nPortIndex,                                         \
         pBuffer)                        /* Macro End */
@@ -1153,7 +1153,7 @@
 #define OMX_EmptyThisBuffer(                                \
         hComponent,                                         \
         pBuffer)                                            \
-    ((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer(      \
+    ((OMX_COMPONENTTYPE*)(hComponent))->EmptyThisBuffer(    \
         hComponent,                                         \
         pBuffer)                        /* Macro End */
 
@@ -1183,7 +1183,7 @@
 #define OMX_FillThisBuffer(                                 \
         hComponent,                                         \
         pBuffer)                                            \
-    ((OMX_COMPONENTTYPE*)hComponent)->FillThisBuffer(       \
+    ((OMX_COMPONENTTYPE*)(hComponent))->FillThisBuffer(     \
         hComponent,                                         \
         pBuffer)                        /* Macro End */
 
@@ -1225,7 +1225,7 @@
            nPortIndex,                                      \
            pAppPrivate,                                     \
            eglImage)                                        \
-    ((OMX_COMPONENTTYPE*)hComponent)->UseEGLImage(          \
+    ((OMX_COMPONENTTYPE*)(hComponent))->UseEGLImage(        \
            hComponent,                                      \
            ppBufferHdr,                                     \
            nPortIndex,                                      \
diff --git a/include/media/openmax/OMX_Index.h b/include/media/openmax/OMX_Index.h
index 1a2a548..5be1355 100644
--- a/include/media/openmax/OMX_Index.h
+++ b/include/media/openmax/OMX_Index.h
@@ -98,10 +98,13 @@
     OMX_IndexParamMetadataKeyFilter,        /**< reference: OMX_PARAM_METADATAFILTERTYPE */
     OMX_IndexConfigPriorityMgmt,            /**< reference: OMX_PRIORITYMGMTTYPE */
     OMX_IndexParamStandardComponentRole,    /**< reference: OMX_PARAM_COMPONENTROLETYPE */
+    OMX_IndexComponentEndUnused,
 
     OMX_IndexPortStartUnused = 0x02000000,
     OMX_IndexParamPortDefinition,           /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */
     OMX_IndexParamCompBufferSupplier,       /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */
+    OMX_IndexPortEndUnused,
+
     OMX_IndexReservedStartUnused = 0x03000000,
 
     /* Audio parameters and configurations */
@@ -134,6 +137,7 @@
     OMX_IndexParamAudioSmv,                 /**< reference: OMX_AUDIO_PARAM_SMVTYPE */
     OMX_IndexParamAudioVorbis,              /**< reference: OMX_AUDIO_PARAM_VORBISTYPE */
     OMX_IndexParamAudioFlac,                /**< reference: OMX_AUDIO_PARAM_FLACTYPE */
+    OMX_IndexAudioEndUnused,
 
     OMX_IndexConfigAudioMidiImmediateEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE */
     OMX_IndexConfigAudioMidiControl,        /**< reference: OMX_AUDIO_CONFIG_MIDICONTROLTYPE */
@@ -194,6 +198,7 @@
     OMX_IndexParamVideoSliceFMO,            /**< reference: OMX_VIDEO_PARAM_AVCSLICEFMO */
     OMX_IndexConfigVideoAVCIntraPeriod,     /**< reference: OMX_VIDEO_CONFIG_AVCINTRAPERIOD */
     OMX_IndexConfigVideoNalSize,            /**< reference: OMX_VIDEO_CONFIG_NALSIZE */
+    OMX_IndexVideoEndUnused,
 
     /* Image & Video common Configurations */
     OMX_IndexCommonStartUnused = 0x07000000,
@@ -231,6 +236,7 @@
     OMX_IndexConfigCommonFocusRegion,       /**< reference: OMX_CONFIG_FOCUSREGIONTYPE */
     OMX_IndexConfigCommonFocusStatus,       /**< reference: OMX_PARAM_FOCUSSTATUSTYPE */
     OMX_IndexConfigCommonTransitionEffect,  /**< reference: OMX_CONFIG_TRANSITIONEFFECTTYPE */
+    OMX_IndexCommonEndUnused,
 
     /* Reserved Configuration range */
     OMX_IndexOtherStartUnused = 0x08000000,
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index b688d1d..5a029d0 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -51,6 +51,7 @@
     OMX_IndexConfigCallbackRequest,                 /**< reference: OMX_CONFIG_CALLBACKREQUESTTYPE */
     OMX_IndexConfigCommitMode,                      /**< reference: OMX_CONFIG_COMMITMODETYPE */
     OMX_IndexConfigCommit,                          /**< reference: OMX_CONFIG_COMMITTYPE */
+    OMX_IndexConfigAndroidVendorExtension,          /**< reference: OMX_CONFIG_VENDOR_EXTENSIONTYPE */
 
     /* Port parameters and configurations */
     OMX_IndexExtPortStartUnused = OMX_IndexKhronosExtensions + 0x00200000,
@@ -62,6 +63,7 @@
     OMX_IndexParamAudioAndroidAacPresentation,      /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
     OMX_IndexParamAudioAndroidEac3,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
     OMX_IndexParamAudioProfileQuerySupported,       /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
+    OMX_IndexExtAudioEndUnused,
 
     /* Image parameters and configurations */
     OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
@@ -80,6 +82,10 @@
     OMX_IndexConfigAndroidIntraRefresh,             /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */
     OMX_IndexParamAndroidVideoTemporalLayering,     /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */
     OMX_IndexConfigAndroidVideoTemporalLayering,    /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */
+    OMX_IndexParamMaxFrameDurationForBitrateControl,/**< reference: OMX_PARAM_U32TYPE */
+    OMX_IndexParamVideoVp9,                         /**< reference: OMX_VIDEO_PARAM_VP9TYPE */
+    OMX_IndexParamVideoAndroidVp9Encoder,           /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */
+    OMX_IndexExtVideoEndUnused,
 
     /* Image & Video common configurations */
     OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
@@ -90,6 +96,8 @@
     OMX_IndexConfigPriority,                        /**< reference: OMX_PARAM_U32TYPE */
     OMX_IndexConfigOperatingRate,                   /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */
     OMX_IndexParamConsumerUsageBits,                /**< reference: OMX_PARAM_U32TYPE */
+    OMX_IndexConfigLatency,                         /**< reference: OMX_PARAM_U32TYPE */
+    OMX_IndexExtOtherEndUnused,
 
     /* Time configurations */
     OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
@@ -97,6 +105,117 @@
     OMX_IndexExtMax = 0x7FFFFFFF
 } OMX_INDEXEXTTYPE;
 
+#define OMX_MAX_STRINGVALUE_SIZE OMX_MAX_STRINGNAME_SIZE
+#define OMX_MAX_ANDROID_VENDOR_PARAMCOUNT 32
+
+typedef enum OMX_ANDROID_VENDOR_VALUETYPE {
+    OMX_AndroidVendorValueInt32 = 0,   /*<< int32_t value */
+    OMX_AndroidVendorValueInt64,       /*<< int64_t value */
+    OMX_AndroidVendorValueString,      /*<< string value */
+    OMX_AndroidVendorValueEndUnused,
+} OMX_ANDROID_VENDOR_VALUETYPE;
+
+/**
+ * Structure describing a single value of an Android vendor extension.
+ *
+ * STRUCTURE MEMBERS:
+ *  cKey        : parameter value name.
+ *  eValueType  : parameter value type
+ *  bSet        : if false, the parameter is not set (for OMX_GetConfig) or is unset (OMX_SetConfig)
+ *                if true, the parameter is set to the corresponding value below
+ *  nInt64      : int64 value
+ *  cString     : string value
+ */
+typedef struct OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE {
+    OMX_U8 cKey[OMX_MAX_STRINGNAME_SIZE];
+    OMX_ANDROID_VENDOR_VALUETYPE eValueType;
+    OMX_BOOL bSet;
+    union {
+        OMX_S32 nInt32;
+        OMX_S64 nInt64;
+        OMX_U8 cString[OMX_MAX_STRINGVALUE_SIZE];
+    };
+} OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE;
+
+/**
+ * OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE is the structure for an Android vendor extension
+ * supported by the component. This structure enumerates the various extension parameters and their
+ * values.
+ *
+ * Android vendor extensions have a name and one or more parameter values - each with a string key -
+ * that are set together. The values are exposed to Android applications via a string key that is
+ * the concatenation of 'vendor', the extension name and the parameter key, each separated by dot
+ * (.), with any trailing '.value' suffix(es) removed (though optionally allowed).
+ *
+ * Extension names and parameter keys are subject to the following rules:
+ *   - Each SHALL contain a set of lowercase alphanumeric (underscore allowed) tags separated by
+ *     dot (.) or dash (-).
+ *   - The first character of the first tag, and any tag following a dot SHALL not start with a
+ *     digit.
+ *   - Tags 'value', 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number
+ *     of underscores) are prohibited in the extension name.
+ *   - Tags 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number
+ *     of underscores) are prohibited in parameter keys.
+ *   - The tag 'value' (even if trailed and/or followed by any number
+ *     of underscores) is prohibited in parameter keys with the following exception:
+ *     the parameter key may be exactly 'value'
+ *   - The parameter key for extensions with a single parameter value SHALL be 'value'
+ *   - No two extensions SHALL have the same name
+ *   - No extension's name SHALL start with another extension's NAME followed by a dot (.)
+ *   - No two parameters of an extension SHALL have the same key
+ *
+ * This config can be used with both OMX_GetConfig and OMX_SetConfig. In the OMX_GetConfig
+ * case, the caller specifies nIndex and nParamSizeUsed. The component fills in cName,
+ * eDir and nParamCount. Additionally, if nParamSizeUsed is not less than nParamCount, the
+ * component fills out the parameter values (nParam) with the current values for each parameter
+ * of the vendor extension.
+ *
+ * The value of nIndex goes from 0 to N-1, where N is the number of Android vendor extensions
+ * supported by the component. The component does not need to report N as the caller can determine
+ * N by enumerating all extensions supported by the component. The component may not support any
+ * extensions. If there are no more extensions, OMX_GetParameter returns OMX_ErrorNoMore. The
+ * component supplies extensions in the order it wants clients to set them.
+ *
+ * The component SHALL return OMX_ErrorNone for all cases where nIndex is less than N (specifically
+ * even in the case of where nParamCount is greater than nParamSizeUsed).
+ *
+ * In the OMX_SetConfig case the field nIndex is ignored. If the component supports an Android
+ * vendor extension with the name in cName, it SHALL configure the parameter values for that
+ * extension according to the parameters in nParam. nParamCount is the number of valid parameters
+ * in the nParam array, and nParamSizeUsed is the size of the nParam array. (nParamSizeUsed
+ * SHALL be at least nParamCount) Parameters that are part of a vendor extension but are not
+ * in the nParam array are assumed to be unset (this is different from not changed).
+ * All parameter values SHALL have distinct keys in nParam (the component can assume that this
+ * is the case. Otherwise, the actual value for the parameters that are multiply defined can
+ * be any of the set values.)
+ *
+ * Return values in case of OMX_SetConfig:
+ *   OMX_ErrorUnsupportedIndex: the component does not support the extension specified by cName
+ *   OMX_ErrorUnsupportedSetting: the component does not support some or any of the parameters
+ *       (names) specified in nParam
+ *   OMX_ErrorBadParameter: the parameter is invalid (e.g. nParamCount is greater than
+ *       nParamSizeUsed, or some parameter value has invalid type)
+ *
+ * STRUCTURE MEMBERS:
+ *  nSize       : size of the structure in bytes
+ *  nVersion    : OMX specification version information
+ *  cName       : name of vendor extension
+ *  nParamCount : the number of parameter values that are part of this vendor extension
+ *  nParamSizeUsed : the size of nParam
+ *                (must be at least 1 and at most OMX_MAX_ANDROID_VENDOR_PARAMCOUNT)
+ *  param       : the parameter values
+ */
+typedef struct OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nIndex;
+    OMX_U8  cName[OMX_MAX_STRINGNAME_SIZE];
+    OMX_DIRTYPE eDir;
+    OMX_U32 nParamCount;
+    OMX_U32 nParamSizeUsed;
+    OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE param[1];
+} OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 2c02431..128dd2d 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -75,39 +75,6 @@
     OMX_VIDEO_VP8LevelMax = 0x7FFFFFFF
 } OMX_VIDEO_VP8LEVELTYPE;
 
-/** VP9 profiles */
-typedef enum OMX_VIDEO_VP9PROFILETYPE {
-    OMX_VIDEO_VP9Profile0       = 0x1,
-    OMX_VIDEO_VP9Profile1       = 0x2,
-    OMX_VIDEO_VP9Profile2       = 0x4,
-    OMX_VIDEO_VP9Profile3       = 0x8,
-    // HDR profiles also support passing HDR metadata
-    OMX_VIDEO_VP9Profile2HDR    = 0x1000,
-    OMX_VIDEO_VP9Profile3HDR    = 0x2000,
-    OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
-    OMX_VIDEO_VP9ProfileMax     = 0x7FFFFFFF
-} OMX_VIDEO_VP9PROFILETYPE;
-
-/** VP9 levels */
-typedef enum OMX_VIDEO_VP9LEVELTYPE {
-    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;
-
 /** VP8 Param */
 typedef struct OMX_VIDEO_PARAM_VP8TYPE {
     OMX_U32 nSize;
@@ -152,7 +119,7 @@
 } OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE;
 
 /**
- * Android specific VP8 encoder params
+ * Android specific VP8/VP9 encoder params
  *
  * STRUCT MEMBERS:
  *  nSize                      : Size of the structure in bytes
@@ -182,6 +149,59 @@
     OMX_U32 nMaxQuantizer;
 } OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE;
 
+/** VP9 profiles */
+typedef enum OMX_VIDEO_VP9PROFILETYPE {
+    OMX_VIDEO_VP9Profile0 = 0x1,
+    OMX_VIDEO_VP9Profile1 = 0x2,
+    OMX_VIDEO_VP9Profile2 = 0x4,
+    OMX_VIDEO_VP9Profile3 = 0x8,
+    // HDR profiles also support passing HDR metadata
+    OMX_VIDEO_VP9Profile2HDR = 0x1000,
+    OMX_VIDEO_VP9Profile3HDR = 0x2000,
+    OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
+    OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9PROFILETYPE;
+
+/** 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_VP9LevelUnknown = 0x6EFFFFFF,
+    OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_VP9LEVELTYPE;
+
+/**
+* VP9 Parameters.
+*   Encoder specific parameters (decoders should ignore these fields):
+*     - bErrorResilientMode
+*     - nTileRows
+*     - nTileColumns
+*     - bEnableFrameParallelDecoding
+*/
+typedef struct OMX_VIDEO_PARAM_VP9TYPE {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_VIDEO_VP9PROFILETYPE eProfile;
+    OMX_VIDEO_VP9LEVELTYPE eLevel;
+    OMX_BOOL bErrorResilientMode;
+    OMX_U32 nTileRows;
+    OMX_U32 nTileColumns;
+    OMX_BOOL bEnableFrameParallelDecoding;
+} OMX_VIDEO_PARAM_VP9TYPE;
+
 /** HEVC Profile enum type */
 typedef enum OMX_VIDEO_HEVCPROFILETYPE {
     OMX_VIDEO_HEVCProfileUnknown      = 0x0,
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index 461fad7..3c81f0f 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -44,13 +44,15 @@
         NAP                          = IBinder::FIRST_CALL_TRANSACTION + 10,
         IS_INTERACTIVE               = IBinder::FIRST_CALL_TRANSACTION + 11,
         IS_POWER_SAVE_MODE           = IBinder::FIRST_CALL_TRANSACTION + 12,
-        SET_POWER_SAVE_MODE          = IBinder::FIRST_CALL_TRANSACTION + 13,
-        REBOOT                       = IBinder::FIRST_CALL_TRANSACTION + 14,
-        SHUTDOWN                     = IBinder::FIRST_CALL_TRANSACTION + 15,
-        CRASH                        = IBinder::FIRST_CALL_TRANSACTION + 16,
+        GET_POWER_SAVE_STATE         = IBinder::FIRST_CALL_TRANSACTION + 13,
+        SET_POWER_SAVE_MODE          = IBinder::FIRST_CALL_TRANSACTION + 14,
+        REBOOT                       = IBinder::FIRST_CALL_TRANSACTION + 17,
+        REBOOT_SAFE_MODE             = IBinder::FIRST_CALL_TRANSACTION + 18,
+        SHUTDOWN                     = IBinder::FIRST_CALL_TRANSACTION + 19,
+        CRASH                        = IBinder::FIRST_CALL_TRANSACTION + 20,
     };
 
-    DECLARE_META_INTERFACE(PowerManager);
+    DECLARE_META_INTERFACE(PowerManager)
 
     // The parcels created by these methods must be kept in sync with the
     // corresponding methods from IPowerManager.aidl.
diff --git a/include/private/binder b/include/private/binder
new file mode 120000
index 0000000..09e9076
--- /dev/null
+++ b/include/private/binder
@@ -0,0 +1 @@
+../../libs/binder/include/private/binder
\ No newline at end of file
diff --git a/include/private/binder/Static.h b/include/private/binder/Static.h
deleted file mode 100644
index d104646..0000000
--- a/include/private/binder/Static.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// All static variables go here, to control initialization and
-// destruction order in the library.
-
-#include <utils/threads.h>
-
-#include <binder/IBinder.h>
-#include <binder/IMemory.h>
-#include <binder/ProcessState.h>
-#include <binder/IPermissionController.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-// For TextStream.cpp
-extern Vector<int32_t> gTextBuffers;
-
-// For ProcessState.cpp
-extern Mutex gProcessMutex;
-extern sp<ProcessState> gProcess;
-
-// For IServiceManager.cpp
-extern Mutex gDefaultServiceManagerLock;
-extern sp<IServiceManager> gDefaultServiceManager;
-extern sp<IPermissionController> gPermissionController;
-
-}   // namespace android
diff --git a/include/private/binder/binder_module.h b/include/private/binder/binder_module.h
deleted file mode 100644
index a8dd64f..0000000
--- a/include/private/binder/binder_module.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BINDER_MODULE_H_
-#define _BINDER_MODULE_H_
-
-#ifdef __cplusplus
-namespace android {
-#endif
-
-/* obtain structures and constants from the kernel header */
-
-#include <sys/ioctl.h>
-#include <linux/binder.h>
-
-#ifdef __cplusplus
-}   // namespace android
-#endif
-
-#endif // _BINDER_MODULE_H_
diff --git a/include/private/gui/ComposerService.h b/include/private/gui/ComposerService.h
index ff2f9bf..50bd742 100644
--- a/include/private/gui/ComposerService.h
+++ b/include/private/gui/ComposerService.h
@@ -28,7 +28,6 @@
 
 // ---------------------------------------------------------------------------
 
-class IMemoryHeap;
 class ISurfaceComposer;
 
 // ---------------------------------------------------------------------------
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..307c764 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -24,6 +24,7 @@
 
 #include <ui/Region.h>
 #include <ui/Rect.h>
+#include <gui/IGraphicBufferProducer.h>
 
 namespace android {
 
@@ -56,6 +57,9 @@
         eFinalCropChanged           = 0x00000400,
         eOverrideScalingModeChanged = 0x00000800,
         eGeometryAppliesWithResize  = 0x00001000,
+        eReparentChildren           = 0x00002000,
+        eDetachChildren             = 0x00004000,
+        eRelativeLayerChanged       = 0x00008000
     };
 
     layer_state_t()
@@ -74,16 +78,16 @@
     status_t    read(const Parcel& input);
 
             struct matrix22_t {
-                float   dsdx;
-                float   dtdx;
-                float   dsdy;
-                float   dtdy;
+                float   dsdx{0};
+                float   dtdx{0};
+                float   dtdy{0};
+                float   dsdy{0};
             };
             sp<IBinder>     surface;
             uint32_t        what;
             float           x;
             float           y;
-            uint32_t        z;
+            int32_t         z;
             uint32_t        w;
             uint32_t        h;
             uint32_t        layerStack;
@@ -94,9 +98,15 @@
             matrix22_t      matrix;
             Rect            crop;
             Rect            finalCrop;
-            sp<IBinder>     handle;
+            sp<IBinder>     barrierHandle;
+            sp<IBinder>     reparentHandle;
             uint64_t        frameNumber;
             int32_t         overrideScalingMode;
+
+            sp<IGraphicBufferProducer> barrierGbp;
+
+            sp<IBinder>     relativeLayerHandle;
+
             // non POD must be last. see write/read
             Region          transparentRegion;
 };
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index 84eb100..a86c586 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
 #define ANDROID_UI_PRIVATE_REGION_HELPER_H
 
+#include <limits>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -27,10 +28,10 @@
 class region_operator
 {
 public:
-    typedef typename RECT::value_type TYPE;    
-    static const TYPE max_value = 0x7FFFFFF;
+    typedef typename RECT::value_type TYPE;
+    static const TYPE max_value = std::numeric_limits<TYPE>::max();
 
-    /* 
+    /*
      * Common boolean operations:
      * value is computed as 0b101 op 0b110
      *    other boolean operation are possible, simply compute
@@ -53,20 +54,20 @@
         TYPE dy;
         inline region(const region& rhs) 
             : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
-        inline region(RECT const* r, size_t c) 
-            : rects(r), count(c), dx(), dy() { }
-        inline region(RECT const* r, size_t c, TYPE dx, TYPE dy) 
-            : rects(r), count(c), dx(dx), dy(dy) { }
+        inline region(RECT const* _r, size_t _c)
+            : rects(_r), count(_c), dx(), dy() { }
+        inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
+            : rects(_r), count(_c), dx(_dx), dy(_dy) { }
     };
 
     class region_rasterizer {
         friend class region_operator;
         virtual void operator()(const RECT& rect) = 0;
     public:
-        virtual ~region_rasterizer() { };
+        virtual ~region_rasterizer() { }
     };
     
-    inline region_operator(int op, const region& lhs, const region& rhs) 
+    inline region_operator(uint32_t op, const region& lhs, const region& rhs)
         : op_mask(op), spanner(lhs, rhs) 
     {
     }
@@ -79,8 +80,8 @@
             spannerInner.prepare(inside);
             do {
                 TYPE left, right;
-                int inside = spannerInner.next(current.left, current.right);
-                if ((op_mask >> inside) & 1) {
+                int inner_inside = spannerInner.next(current.left, current.right);
+                if ((op_mask >> inner_inside) & 1) {
                     if (current.left < current.right && 
                             current.top < current.bottom) {
                         rasterizer(current);
@@ -162,8 +163,8 @@
         region rhs;
 
     public:
-        inline Spanner(const region& lhs, const region& rhs)
-        : lhs(lhs), rhs(rhs)
+        inline Spanner(const region& _lhs, const region& _rhs)
+        : lhs(_lhs), rhs(_rhs)
         {
             if (lhs.count) {
                 SpannerBase::lhs_head = lhs.rects->top      + lhs.dy;
@@ -223,8 +224,8 @@
         region rhs;
         
     public:
-        inline SpannerInner(const region& lhs, const region& rhs)
-            : lhs(lhs), rhs(rhs) 
+        inline SpannerInner(const region& _lhs, const region& _rhs)
+            : lhs(_lhs), rhs(_rhs)
         {
         }
 
diff --git a/include/ui/ANativeObjectBase.h b/include/ui/ANativeObjectBase.h
index 76e850f..640e34b 100644
--- a/include/ui/ANativeObjectBase.h
+++ b/include/ui/ANativeObjectBase.h
@@ -18,9 +18,7 @@
 #define ANDROID_ANDROID_NATIVES_H
 
 #include <sys/types.h>
-#include <string.h>
 
-#include <hardware/gralloc.h>
 #include <system/window.h>
 
 // ---------------------------------------------------------------------------
diff --git a/include/ui/BufferQueueDefs.h b/include/ui/BufferQueueDefs.h
new file mode 100644
index 0000000..56de181
--- /dev/null
+++ b/include/ui/BufferQueueDefs.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_BUFFERQUEUEDEFS_H
+#define ANDROID_UI_BUFFERQUEUEDEFS_H
+
+namespace android {
+    namespace BufferQueueDefs {
+        // BufferQueue will keep track of at most this value of buffers.
+        // Attempts at runtime to increase the number of buffers past this
+        // will fail.
+        static constexpr int NUM_BUFFER_SLOTS = 64;
+    } // namespace BufferQueueDefs
+} // namespace android
+
+#endif
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
new file mode 100644
index 0000000..8ccf6d3
--- /dev/null
+++ b/include/ui/ColorSpace.h
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_COLOR_SPACE
+#define ANDROID_UI_COLOR_SPACE
+
+#include <array>
+#include <cmath>
+#include <functional>
+#include <memory>
+#include <string>
+
+#include <math/mat3.h>
+#include <math/scalar.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+
+namespace android {
+
+class ColorSpace {
+public:
+    typedef std::function<float(float)> transfer_function;
+    typedef std::function<float(float)> clamping_function;
+
+    struct TransferParameters {
+        float g = 0.0f;
+        float a = 0.0f;
+        float b = 0.0f;
+        float c = 0.0f;
+        float d = 0.0f;
+        float e = 0.0f;
+        float f = 0.0f;
+    };
+
+    /**
+     * Creates a named color space with the specified RGB->XYZ
+     * conversion matrix. The white point and primaries will be
+     * computed from the supplied matrix.
+     *
+     * The default transfer functions are a linear response x->x
+     * and the default clamping function is a simple saturate
+     * (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const mat3& rgbToXYZ,
+            transfer_function OETF = linearResponse,
+            transfer_function EOTF = linearResponse,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified RGB->XYZ
+     * conversion matrix. The white point and primaries will be
+     * computed from the supplied matrix.
+     *
+     * The transfer functions are defined by the set of supplied
+     * transfer parameters. The default clamping function is a
+     * simple saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const mat3& rgbToXYZ,
+            const TransferParameters parameters,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified RGB->XYZ
+     * conversion matrix. The white point and primaries will be
+     * computed from the supplied matrix.
+     *
+     * The transfer functions are defined by a simple gamma value.
+     * The default clamping function is a saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const mat3& rgbToXYZ,
+            float gamma,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified primaries
+     * and white point. The RGB<>XYZ conversion matrices are
+     * computed from the primaries and white point.
+     *
+     * The default transfer functions are a linear response x->x
+     * and the default clamping function is a simple saturate
+     * (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const std::array<float2, 3>& primaries,
+            const float2& whitePoint,
+            transfer_function OETF = linearResponse,
+            transfer_function EOTF = linearResponse,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified primaries
+     * and white point. The RGB<>XYZ conversion matrices are
+     * computed from the primaries and white point.
+     *
+     * The transfer functions are defined by the set of supplied
+     * transfer parameters. The default clamping function is a
+     * simple saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const std::array<float2, 3>& primaries,
+            const float2& whitePoint,
+            const TransferParameters parameters,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified primaries
+     * and white point. The RGB<>XYZ conversion matrices are
+     * computed from the primaries and white point.
+     *
+     * The transfer functions are defined by a single gamma value.
+     * The default clamping function is a saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const std::array<float2, 3>& primaries,
+            const float2& whitePoint,
+            float gamma,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    ColorSpace() noexcept = delete;
+
+    /**
+     * Encodes the supplied RGB value using this color space's
+     * opto-electronic transfer function.
+     */
+    constexpr float3 fromLinear(const float3& v) const noexcept {
+        return apply(v, mOETF);
+    }
+
+    /**
+     * Decodes the supplied RGB value using this color space's
+     * electro-optical transfer function.
+     */
+    constexpr float3 toLinear(const float3& v) const noexcept {
+        return apply(v, mEOTF);
+    }
+
+    /**
+     * Converts the supplied XYZ value to RGB. The returned value
+     * is encoded with this color space's opto-electronic transfer
+     * function and clamped by this color space's clamping function.
+     */
+    constexpr float3 xyzToRGB(const float3& xyz) const noexcept {
+        return apply(fromLinear(mXYZtoRGB * xyz), mClamper);
+    }
+
+    /**
+     * Converts the supplied RGB value to XYZ. The input RGB value
+     * is decoded using this color space's electro-optical function
+     * before being converted to XYZ.
+     */
+    constexpr float3 rgbToXYZ(const float3& rgb) const noexcept {
+        return mRGBtoXYZ * toLinear(rgb);
+    }
+
+    constexpr const std::string& getName() const noexcept {
+        return mName;
+    }
+
+    constexpr const mat3& getRGBtoXYZ() const noexcept {
+        return mRGBtoXYZ;
+    }
+
+    constexpr const mat3& getXYZtoRGB() const noexcept {
+        return mXYZtoRGB;
+    }
+
+    constexpr const transfer_function& getOETF() const noexcept {
+        return mOETF;
+    }
+
+    constexpr const transfer_function& getEOTF() const noexcept {
+        return mEOTF;
+    }
+
+    constexpr const clamping_function& getClamper() const noexcept {
+        return mClamper;
+    }
+
+    constexpr const std::array<float2, 3>& getPrimaries() const noexcept {
+        return mPrimaries;
+    }
+
+    constexpr const float2& getWhitePoint() const noexcept {
+        return mWhitePoint;
+    }
+
+    constexpr const TransferParameters& getTransferParameters() const noexcept {
+        return mParameters;
+    }
+
+    /**
+     * Converts the supplied XYZ value to xyY.
+     */
+    static constexpr float2 xyY(const float3& XYZ) {
+        return XYZ.xy / dot(XYZ, float3{1});
+    }
+
+    /**
+     * Converts the supplied xyY value to XYZ.
+     */
+    static constexpr float3 XYZ(const float3& xyY) {
+        return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y};
+    }
+
+    static const ColorSpace sRGB();
+    static const ColorSpace linearSRGB();
+    static const ColorSpace extendedSRGB();
+    static const ColorSpace linearExtendedSRGB();
+    static const ColorSpace NTSC();
+    static const ColorSpace BT709();
+    static const ColorSpace BT2020();
+    static const ColorSpace AdobeRGB();
+    static const ColorSpace ProPhotoRGB();
+    static const ColorSpace DisplayP3();
+    static const ColorSpace DCIP3();
+    static const ColorSpace ACES();
+    static const ColorSpace ACEScg();
+
+    // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256)
+    // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B
+    // The generated 3D LUT is meant to be used as a 3D texture and its Y
+    // axis is thus already flipped
+    // The source color space must define its values in the domain [0..1]
+    // The generated LUT transforms from gamma space to gamma space
+    static std::unique_ptr<float3> createLUT(uint32_t size,
+            const ColorSpace& src, const ColorSpace& dst);
+
+private:
+    static constexpr mat3 computeXYZMatrix(
+            const std::array<float2, 3>& primaries, const float2& whitePoint);
+
+    static constexpr float linearResponse(float v) {
+        return v;
+    }
+
+    std::string mName;
+
+    mat3 mRGBtoXYZ;
+    mat3 mXYZtoRGB;
+
+    TransferParameters mParameters;
+    transfer_function mOETF;
+    transfer_function mEOTF;
+    clamping_function mClamper;
+
+    std::array<float2, 3> mPrimaries;
+    float2 mWhitePoint;
+};
+
+class ColorSpaceConnector {
+public:
+    ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept;
+
+    constexpr const ColorSpace& getSource() const noexcept { return mSource; }
+    constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
+
+    constexpr const mat3& getTransform() const noexcept { return mTransform; }
+
+    constexpr float3 transform(const float3& v) const noexcept {
+        float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
+        return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
+    }
+
+    constexpr float3 transformLinear(const float3& v) const noexcept {
+        float3 linear = apply(v, mSource.getClamper());
+        return apply(mTransform * linear, mDestination.getClamper());
+    }
+
+private:
+    ColorSpace mSource;
+    ColorSpace mDestination;
+    mat3 mTransform;
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_COLOR_SPACE
diff --git a/include/ui/DebugUtils.h b/include/ui/DebugUtils.h
new file mode 100644
index 0000000..8483808
--- /dev/null
+++ b/include/ui/DebugUtils.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <system/graphics.h>
+
+#include <string>
+
+std::string decodeStandard(android_dataspace dataspace);
+std::string decodeTransfer(android_dataspace dataspace);
+std::string decodeRange(android_dataspace dataspace);
+std::string dataspaceDetails(android_dataspace dataspace);
+std::string decodeColorMode(android_color_mode colormode);
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index 799944f..94caf6b 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -19,23 +19,22 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/Timers.h>
 
-#include <ui/PixelFormat.h>
+#include <utils/Timers.h>
 
 namespace android {
 
 struct DisplayInfo {
-    uint32_t w;
-    uint32_t h;
-    float xdpi;
-    float ydpi;
-    float fps;
-    float density;
-    uint8_t orientation;
-    bool secure;
-    nsecs_t appVsyncOffset;
-    nsecs_t presentationDeadline;
+    uint32_t w{0};
+    uint32_t h{0};
+    float xdpi{0};
+    float ydpi{0};
+    float fps{0};
+    float density{0};
+    uint8_t orientation{0};
+    bool secure{false};
+    nsecs_t appVsyncOffset{0};
+    nsecs_t presentationDeadline{0};
 };
 
 /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/include/ui/DisplayStatInfo.h b/include/ui/DisplayStatInfo.h
index 0549a83..09543ec 100644
--- a/include/ui/DisplayStatInfo.h
+++ b/include/ui/DisplayStatInfo.h
@@ -22,8 +22,8 @@
 namespace android {
 
 struct DisplayStatInfo {
-    nsecs_t vsyncTime;
-    nsecs_t vsyncPeriod;
+    nsecs_t vsyncTime{0};
+    nsecs_t vsyncPeriod{0};
 };
 
 }; // namespace android
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 48a7aa3..37811bc 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,15 @@
 #define ANDROID_FENCE_H
 
 #include <stdint.h>
-#include <sys/types.h>
 
-#include <ui/ANativeObjectBase.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
 #include <utils/Flattenable.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include <experimental/optional>
-
-struct ANativeWindowBuffer;
-
 namespace android {
 
+class String8;
+
 // ===========================================================================
 // Fence
 // ===========================================================================
@@ -42,6 +36,11 @@
 {
 public:
     static const sp<Fence> NO_FENCE;
+    static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+    static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+    static inline bool isValidTimestamp(nsecs_t time) {
+        return time >= 0 && time < INT64_MAX;
+    }
 
     // TIMEOUT_NEVER may be passed to the wait method to indicate that it
     // should wait indefinitely for the fence to signal.
@@ -55,7 +54,13 @@
     // Construct a new Fence object to manage a given fence file descriptor.
     // When the new Fence object is destructed the file descriptor will be
     // closed.
-    Fence(int fenceFd);
+    explicit Fence(int fenceFd);
+
+    // Not copyable or movable.
+    Fence(const Fence& rhs) = delete;
+    Fence& operator=(const Fence& rhs) = delete;
+    Fence(Fence&& rhs) = delete;
+    Fence& operator=(Fence&& rhs) = delete;
 
     // Check whether the Fence has an open fence file descriptor. Most Fence
     // methods treat an invalid file descriptor just like a valid fence that
@@ -94,30 +99,33 @@
 
     // getSignalTime returns the system monotonic clock time at which the
     // fence transitioned to the signaled state.  If the fence is not signaled
-    // then INT64_MAX is returned.  If the fence is invalid or if an error
-    // occurs then -1 is returned.
+    // then SIGNAL_TIME_PENDING is returned.  If the fence is invalid or if an
+    // error occurs then SIGNAL_TIME_INVALID is returned.
     nsecs_t getSignalTime() const;
 
-#if __cplusplus > 201103L
-    // hasSignaled returns whether the fence has signaled yet. Prefer this to
+    enum class Status {
+        Invalid,     // Fence is invalid
+        Unsignaled,  // Fence is valid but has not yet signaled
+        Signaled,    // Fence is valid and has signaled
+    };
+
+    // getStatus() returns whether the fence has signaled yet. Prefer this to
     // getSignalTime() or wait() if all you care about is whether the fence has
-    // signaled. Returns an optional bool, which will have a value if there was
-    // no error.
-    inline std::experimental::optional<bool> hasSignaled() {
+    // signaled.
+    inline Status getStatus() {
         // The sync_wait call underlying wait() has been measured to be
         // significantly faster than the sync_fence_info call underlying
         // getSignalTime(), which might otherwise appear to be the more obvious
         // way to check whether a fence has signaled.
         switch (wait(0)) {
             case NO_ERROR:
-                return true;
+                return Status::Signaled;
             case -ETIME:
-                return false;
+                return Status::Unsignaled;
             default:
-                return {};
+                return Status::Invalid;
         }
     }
-#endif
 
     // Flattenable interface
     size_t getFlattenedSize() const;
@@ -130,11 +138,6 @@
     friend class LightRefBase<Fence>;
     ~Fence();
 
-    // Disallow copying
-    Fence(const Fence& rhs);
-    Fence& operator = (const Fence& rhs);
-    const Fence& operator = (const Fence& rhs) const;
-
     int mFenceFd;
 };
 
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
new file mode 100644
index 0000000..871fcf2
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FENCE_TIME_H
+#define ANDROID_FENCE_TIME_H
+
+#include <ui/Fence.h>
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+
+#include <atomic>
+#include <mutex>
+#include <queue>
+#include <unordered_map>
+
+namespace android {
+
+class FenceToFenceTimeMap;
+
+// A wrapper around fence that only implements isValid and getSignalTime.
+// It automatically closes the fence in a thread-safe manner once the signal
+// time is known.
+class FenceTime {
+friend class FenceToFenceTimeMap;
+public:
+    // An atomic snapshot of the FenceTime that is flattenable.
+    //
+    // This class is needed because the FenceTime class may not stay
+    // consistent for all steps of the flattening process.
+    //
+    // Not thread safe.
+    struct Snapshot : public Flattenable<Snapshot> {
+        enum class State {
+            EMPTY,
+            FENCE,
+            SIGNAL_TIME,
+        };
+
+        Snapshot() = default;  // Creates an empty snapshot.
+        explicit Snapshot(const sp<Fence>& fence);
+        explicit Snapshot(nsecs_t signalTime);
+
+        // Movable.
+        Snapshot(Snapshot&& src) = default;
+        Snapshot& operator=(Snapshot&& src) = default;
+        // Not copyable.
+        Snapshot(const Snapshot& src) = delete;
+        Snapshot& operator=(const Snapshot&& src) = delete;
+
+        // Flattenable implementation.
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds,
+                size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+                size_t& count);
+
+        State state{State::EMPTY};
+        sp<Fence> fence{Fence::NO_FENCE};
+        nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID};
+    };
+
+    static const std::shared_ptr<FenceTime> NO_FENCE;
+
+    explicit FenceTime(const sp<Fence>& fence);
+    explicit FenceTime(sp<Fence>&& fence);
+
+    // Passing in Fence::SIGNAL_TIME_PENDING is not allowed.
+    // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID.
+    explicit FenceTime(nsecs_t signalTime);
+
+    // Do not allow default construction. Share NO_FENCE or explicitly construct
+    // with Fence::SIGNAL_TIME_INVALID instead.
+    FenceTime() = delete;
+
+    // Do not allow copy, assign, or move. Use a shared_ptr to share the
+    // signalTime result. Or use getSnapshot() if a thread-safe copy is really
+    // needed.
+    FenceTime(const FenceTime&) = delete;
+    FenceTime(FenceTime&&) = delete;
+    FenceTime& operator=(const FenceTime&) = delete;
+    FenceTime& operator=(FenceTime&&) = delete;
+
+    // This method should only be called when replacing the fence with
+    // a signalTime. Since this is an indirect way of setting the signal time
+    // of a fence, the snapshot should come from a trusted source.
+    void applyTrustedSnapshot(const Snapshot& src);
+
+    bool isValid() const;
+
+    // Attempts to get the timestamp from the Fence if the timestamp isn't
+    // already cached. Otherwise, it returns the cached value.
+    nsecs_t getSignalTime();
+
+    // Gets the cached timestamp without attempting to query the Fence.
+    nsecs_t getCachedSignalTime() const;
+
+    // Returns a snapshot of the FenceTime in its current state.
+    Snapshot getSnapshot() const;
+
+    void signalForTest(nsecs_t signalTime);
+
+    // Override new and delete since this needs 8-byte alignment, which
+    // is not guaranteed on x86.
+    static void* operator new(size_t nbytes) noexcept;
+    static void operator delete(void *p);
+
+private:
+    // For tests only. If forceValidForTest is true, then getSignalTime will
+    // never return SIGNAL_TIME_INVALID and isValid will always return true.
+    FenceTime(const sp<Fence>& fence, bool forceValidForTest);
+
+    enum class State {
+        VALID,
+        INVALID,
+        FORCED_VALID_FOR_TEST,
+    };
+
+    const State mState{State::INVALID};
+
+    // mMutex guards mFence and mSignalTime.
+    // mSignalTime is also atomic since it is sometimes read outside the lock
+    // for quick checks.
+    mutable std::mutex mMutex;
+    sp<Fence> mFence{Fence::NO_FENCE};
+    std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
+};
+
+// A queue of FenceTimes that are expected to signal in FIFO order.
+// Only maintains a queue of weak pointers so it doesn't keep references
+// to Fences on its own.
+//
+// Can be used to get the signal time of a fence and close its file descriptor
+// without making a syscall for every fence later in the timeline.
+// Additionally, since the FenceTime caches the timestamp internally,
+// other timelines that reference the same FenceTime can avoid the syscall.
+//
+// FenceTimeline only keeps track of a limited number of entries to avoid
+// growing unbounded. Users of FenceTime must make sure they can work even
+// if FenceTimeline did nothing. i.e. they should eventually call
+// Fence::getSignalTime(), not only Fence::getCachedSignalTime().
+//
+// push() and updateSignalTimes() are safe to call simultaneously from
+// different threads.
+class FenceTimeline {
+public:
+    static constexpr size_t MAX_ENTRIES = 64;
+
+    void push(const std::shared_ptr<FenceTime>& fence);
+    void updateSignalTimes();
+
+private:
+    mutable std::mutex mMutex;
+    std::queue<std::weak_ptr<FenceTime>> mQueue;
+};
+
+// Used by test code to create or get FenceTimes for a given Fence.
+//
+// By design, Fences cannot be signaled from user space. However, this class
+// allows test code to set the apparent signalTime of a Fence and
+// have it be visible to all FenceTimes. Release code should not use
+// FenceToFenceTimeMap.
+//
+// FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically
+// garbage collects entries every time a new FenceTime is created to avoid
+// leaks. This prevents us from having to make the Fence destructor
+// automatically notify that the underlying fence has been destroyed, which
+// would affect release code paths. Garbage collecting so often is inefficient,
+// but acceptable for testing.
+//
+// Since FenceTimes maintain a strong reference to underlying Fences, there
+// should not be any aliasing issues where a new Fence happens to have the same
+// address as a previous Fence; the previous entry will be garbage collected
+// before the new one is added.
+class FenceToFenceTimeMap {
+public:
+    // Create a new FenceTime with that wraps the provided Fence.
+    std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+
+    // Signals all FenceTimes created through this class that are wrappers
+    // around |fence|.
+    void signalAllForTest(const sp<Fence>& fence, nsecs_t signalTime);
+
+private:
+    // Cleans up the entries that no longer have a strong reference.
+    void garbageCollectLocked();
+
+    mutable std::mutex mMutex;
+    std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 100644
index 0000000..270675c
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+class FloatRect {
+public:
+    FloatRect() = default;
+    constexpr FloatRect(float _left, float _top, float _right, float _bottom)
+      : left(_left), top(_top), right(_right), bottom(_bottom) {}
+
+    float getWidth() const { return right - left; }
+    float getHeight() const { return bottom - top; }
+
+    float left = 0.0f;
+    float top = 0.0f;
+    float right = 0.0f;
+    float bottom = 0.0f;
+};
+
+inline bool operator==(const FloatRect& a, const FloatRect& b) {
+    return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
+}
+
+}  // namespace android
diff --git a/include/ui/FrameStats.h b/include/ui/FrameStats.h
index 6bfe635..bc9d3ec 100644
--- a/include/ui/FrameStats.h
+++ b/include/ui/FrameStats.h
@@ -25,7 +25,7 @@
 
 class FrameStats : public LightFlattenable<FrameStats> {
 public:
-    FrameStats() : refreshPeriodNano(0) {};
+    FrameStats() : refreshPeriodNano(0) {}
 
     /*
      * Approximate refresh time, in nanoseconds.
diff --git a/include/ui/Gralloc1.h b/include/ui/Gralloc1.h
deleted file mode 100644
index cf8c173..0000000
--- a/include/ui/Gralloc1.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UI_GRALLOC1_H
-#define ANDROID_UI_GRALLOC1_H
-
-#define GRALLOC1_LOG_TAG "Gralloc1"
-
-#include <ui/Gralloc1On0Adapter.h>
-
-#include <unordered_set>
-
-namespace std {
-    template <>
-    struct hash<gralloc1_capability_t> {
-        size_t operator()(gralloc1_capability_t capability) const {
-            return std::hash<int32_t>()(static_cast<int32_t>(capability));
-        }
-    };
-}
-
-namespace android {
-
-class Fence;
-class GraphicBuffer;
-
-namespace Gralloc1 {
-
-class Device;
-
-class Descriptor {
-public:
-    Descriptor(Device& device, gralloc1_buffer_descriptor_t deviceId)
-      : mShimDevice(device),
-        mDeviceId(deviceId),
-        mWidth(0),
-        mHeight(0),
-        mFormat(static_cast<android_pixel_format_t>(0)),
-        mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
-        mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
-
-    ~Descriptor();
-
-    gralloc1_buffer_descriptor_t getDeviceId() const { return mDeviceId; }
-
-    gralloc1_error_t setDimensions(uint32_t width, uint32_t height);
-    gralloc1_error_t setFormat(android_pixel_format_t format);
-    gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage);
-    gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage);
-
-private:
-    Device& mShimDevice;
-    const gralloc1_buffer_descriptor_t mDeviceId;
-
-    uint32_t mWidth;
-    uint32_t mHeight;
-    android_pixel_format_t mFormat;
-    gralloc1_producer_usage_t mProducerUsage;
-    gralloc1_consumer_usage_t mConsumerUsage;
-
-}; // Descriptor
-
-class Device {
-    friend class Gralloc1::Descriptor;
-
-public:
-    Device(gralloc1_device_t* device);
-
-    bool hasCapability(gralloc1_capability_t capability) const;
-
-    std::string dump();
-
-    std::shared_ptr<Descriptor> createDescriptor();
-
-    gralloc1_error_t getStride(buffer_handle_t buffer, uint32_t* outStride);
-
-    gralloc1_error_t allocate(
-            const std::vector<std::shared_ptr<const Descriptor>>& descriptors,
-            std::vector<buffer_handle_t>* outBuffers);
-    gralloc1_error_t allocate(
-            const std::shared_ptr<const Descriptor>& descriptor,
-            gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-
-    gralloc1_error_t retain(buffer_handle_t buffer);
-    gralloc1_error_t retain(const GraphicBuffer* buffer);
-
-    gralloc1_error_t release(buffer_handle_t buffer);
-
-    gralloc1_error_t getNumFlexPlanes(buffer_handle_t buffer,
-            uint32_t* outNumPlanes);
-
-    gralloc1_error_t lock(buffer_handle_t buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t* accessRegion, void** outData,
-            const sp<Fence>& acquireFence);
-    gralloc1_error_t lockFlex(buffer_handle_t buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t* accessRegion,
-            struct android_flex_layout* outData, const sp<Fence>& acquireFence);
-    gralloc1_error_t lockYCbCr(buffer_handle_t buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t* accessRegion, struct android_ycbcr* outData,
-            const sp<Fence>& acquireFence);
-
-    gralloc1_error_t unlock(buffer_handle_t buffer, sp<Fence>* outFence);
-
-private:
-    std::unordered_set<gralloc1_capability_t> loadCapabilities();
-
-    bool loadFunctions();
-
-    template <typename LockType, typename OutType>
-    gralloc1_error_t lockHelper(LockType pfn, buffer_handle_t buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t* accessRegion, OutType* outData,
-            const sp<Fence>& acquireFence) {
-        int32_t intError = pfn(mDevice, buffer,
-                static_cast<uint64_t>(producerUsage),
-                static_cast<uint64_t>(consumerUsage), accessRegion, outData,
-                acquireFence->dup());
-        return static_cast<gralloc1_error_t>(intError);
-    }
-
-    gralloc1_device_t* const mDevice;
-
-    const std::unordered_set<gralloc1_capability_t> mCapabilities;
-
-    template <typename PFN, gralloc1_function_descriptor_t descriptor>
-    struct FunctionLoader {
-        FunctionLoader() : pfn(nullptr) {}
-
-        bool load(gralloc1_device_t* device, bool errorIfNull) {
-            gralloc1_function_pointer_t rawPointer =
-                    device->getFunction(device, descriptor);
-            pfn = reinterpret_cast<PFN>(rawPointer);
-            if (errorIfNull && !rawPointer) {
-                ALOG(LOG_ERROR, GRALLOC1_LOG_TAG,
-                        "Failed to load function pointer %d", descriptor);
-            }
-            return rawPointer != nullptr;
-        }
-
-        template <typename ...Args>
-        typename std::result_of<PFN(Args...)>::type operator()(Args... args) {
-            return pfn(args...);
-        }
-
-        PFN pfn;
-    };
-
-    // Function pointers
-    struct Functions {
-        FunctionLoader<GRALLOC1_PFN_DUMP, GRALLOC1_FUNCTION_DUMP> dump;
-        FunctionLoader<GRALLOC1_PFN_CREATE_DESCRIPTOR,
-                GRALLOC1_FUNCTION_CREATE_DESCRIPTOR> createDescriptor;
-        FunctionLoader<GRALLOC1_PFN_DESTROY_DESCRIPTOR,
-                GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR> destroyDescriptor;
-        FunctionLoader<GRALLOC1_PFN_SET_CONSUMER_USAGE,
-                GRALLOC1_FUNCTION_SET_CONSUMER_USAGE> setConsumerUsage;
-        FunctionLoader<GRALLOC1_PFN_SET_DIMENSIONS,
-                GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions;
-        FunctionLoader<GRALLOC1_PFN_SET_FORMAT,
-                GRALLOC1_FUNCTION_SET_FORMAT> setFormat;
-        FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE,
-                GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage;
-        FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE,
-                GRALLOC1_FUNCTION_GET_BACKING_STORE> getBackingStore;
-        FunctionLoader<GRALLOC1_PFN_GET_CONSUMER_USAGE,
-                GRALLOC1_FUNCTION_GET_CONSUMER_USAGE> getConsumerUsage;
-        FunctionLoader<GRALLOC1_PFN_GET_DIMENSIONS,
-                GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions;
-        FunctionLoader<GRALLOC1_PFN_GET_FORMAT,
-                GRALLOC1_FUNCTION_GET_FORMAT> getFormat;
-        FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE,
-                GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage;
-        FunctionLoader<GRALLOC1_PFN_GET_STRIDE,
-                GRALLOC1_FUNCTION_GET_STRIDE> getStride;
-        FunctionLoader<GRALLOC1_PFN_ALLOCATE,
-                GRALLOC1_FUNCTION_ALLOCATE> allocate;
-        FunctionLoader<GRALLOC1_PFN_RETAIN,
-                GRALLOC1_FUNCTION_RETAIN> retain;
-        FunctionLoader<GRALLOC1_PFN_RELEASE,
-                GRALLOC1_FUNCTION_RELEASE> release;
-        FunctionLoader<GRALLOC1_PFN_GET_NUM_FLEX_PLANES,
-                GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES> getNumFlexPlanes;
-        FunctionLoader<GRALLOC1_PFN_LOCK,
-                GRALLOC1_FUNCTION_LOCK> lock;
-        FunctionLoader<GRALLOC1_PFN_LOCK_FLEX,
-                GRALLOC1_FUNCTION_LOCK_FLEX> lockFlex;
-        FunctionLoader<GRALLOC1_PFN_LOCK_YCBCR,
-                GRALLOC1_FUNCTION_LOCK_YCBCR> lockYCbCr;
-        FunctionLoader<GRALLOC1_PFN_UNLOCK,
-                GRALLOC1_FUNCTION_UNLOCK> unlock;
-
-        // Adapter-only functions
-        FunctionLoader<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER,
-                GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER> retainGraphicBuffer;
-        FunctionLoader<GRALLOC1_PFN_ALLOCATE_WITH_ID,
-                GRALLOC1_FUNCTION_ALLOCATE_WITH_ID> allocateWithId;
-    } mFunctions;
-
-}; // class android::Gralloc1::Device
-
-class Loader
-{
-public:
-    Loader();
-    ~Loader();
-
-    std::unique_ptr<Device> getDevice();
-
-private:
-    static std::unique_ptr<Gralloc1On0Adapter> mAdapter;
-    std::unique_ptr<Device> mDevice;
-};
-
-} // namespace android::Gralloc1
-
-} // namespace android
-
-#endif
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
deleted file mode 100644
index 97c9a89..0000000
--- a/include/ui/Gralloc1On0Adapter.h
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
-#define ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
-
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-#include <hardware/gralloc1.h>
-
-#include <mutex>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-struct gralloc_module_t;
-
-// This is not an "official" capability (i.e., it is not found in gralloc1.h),
-// but we will use it to detect that we are running through the adapter, which
-// is capable of collaborating with GraphicBuffer such that queries on a
-// buffer_handle_t succeed
-static const auto GRALLOC1_CAPABILITY_ON_ADAPTER =
-        static_cast<gralloc1_capability_t>(GRALLOC1_LAST_CAPABILITY + 1);
-
-static const auto GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 1);
-static const auto GRALLOC1_FUNCTION_ALLOCATE_WITH_ID =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 2);
-static const auto GRALLOC1_FUNCTION_LOCK_YCBCR =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 3);
-static const auto GRALLOC1_LAST_ADAPTER_FUNCTION = GRALLOC1_FUNCTION_LOCK_YCBCR;
-
-typedef gralloc1_error_t (*GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER)(
-        gralloc1_device_t* device, const android::GraphicBuffer* buffer);
-typedef gralloc1_error_t (*GRALLOC1_PFN_ALLOCATE_WITH_ID)(
-        gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptor,
-        gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_LOCK_YCBCR)(
-        gralloc1_device_t* device, buffer_handle_t buffer,
-        uint64_t /*gralloc1_producer_usage_t*/ producerUsage,
-        uint64_t /*gralloc1_consumer_usage_t*/ consumerUsage,
-        const gralloc1_rect_t* accessRegion, struct android_ycbcr* outYCbCr,
-        int32_t acquireFence);
-
-namespace android {
-
-class Gralloc1On0Adapter : public gralloc1_device_t
-{
-public:
-    Gralloc1On0Adapter(const hw_module_t* module);
-    ~Gralloc1On0Adapter();
-
-    gralloc1_device_t* getDevice() {
-        return static_cast<gralloc1_device_t*>(this);
-    }
-
-private:
-    static inline Gralloc1On0Adapter* getAdapter(gralloc1_device_t* device) {
-        return static_cast<Gralloc1On0Adapter*>(device);
-    }
-
-    // getCapabilities
-
-    void doGetCapabilities(uint32_t* outCount,
-            int32_t* /*gralloc1_capability_t*/ outCapabilities);
-    static void getCapabilitiesHook(gralloc1_device_t* device,
-            uint32_t* outCount,
-            int32_t* /*gralloc1_capability_t*/ outCapabilities) {
-        getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
-    };
-
-    // getFunction
-
-    gralloc1_function_pointer_t doGetFunction(
-            int32_t /*gralloc1_function_descriptor_t*/ descriptor);
-    static gralloc1_function_pointer_t getFunctionHook(
-            gralloc1_device_t* device,
-            int32_t /*gralloc1_function_descriptor_t*/ descriptor) {
-        return getAdapter(device)->doGetFunction(descriptor);
-    }
-
-    // dump
-
-    void dump(uint32_t* outSize, char* outBuffer);
-    static void dumpHook(gralloc1_device_t* device, uint32_t* outSize,
-            char* outBuffer) {
-        return getAdapter(device)->dump(outSize, outBuffer);
-    }
-    std::string mCachedDump;
-
-    // Buffer descriptor lifecycle functions
-
-    class Descriptor;
-
-    gralloc1_error_t createDescriptor(
-            gralloc1_buffer_descriptor_t* outDescriptor);
-    static int32_t createDescriptorHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t* outDescriptor) {
-        auto error = getAdapter(device)->createDescriptor(outDescriptor);
-        return static_cast<int32_t>(error);
-    }
-
-    gralloc1_error_t destroyDescriptor(gralloc1_buffer_descriptor_t descriptor);
-    static int32_t destroyDescriptorHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptor) {
-        auto error = getAdapter(device)->destroyDescriptor(descriptor);
-        return static_cast<int32_t>(error);
-    }
-
-    // Buffer descriptor modification functions
-
-    struct Descriptor : public std::enable_shared_from_this<Descriptor> {
-        Descriptor(Gralloc1On0Adapter* adapter,
-                gralloc1_buffer_descriptor_t id)
-          : adapter(adapter),
-            id(id),
-            width(0),
-            height(0),
-            format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
-            producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
-            consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
-
-        gralloc1_error_t setDimensions(uint32_t w, uint32_t h) {
-            width = w;
-            height = h;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t setFormat(int32_t f) {
-            format = f;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
-            producerUsage = usage;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage) {
-            consumerUsage = usage;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        Gralloc1On0Adapter* const adapter;
-        const gralloc1_buffer_descriptor_t id;
-
-        uint32_t width;
-        uint32_t height;
-        int32_t format;
-        gralloc1_producer_usage_t producerUsage;
-        gralloc1_consumer_usage_t consumerUsage;
-    };
-
-    template <typename ...Args>
-    static int32_t callDescriptorFunction(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptorId,
-            gralloc1_error_t (Descriptor::*member)(Args...), Args... args) {
-        auto descriptor = getAdapter(device)->getDescriptor(descriptorId);
-        if (!descriptor) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_DESCRIPTOR);
-        }
-        auto error = ((*descriptor).*member)(std::forward<Args>(args)...);
-        return static_cast<int32_t>(error);
-    }
-
-    static int32_t setConsumerUsageHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
-        auto usage = static_cast<gralloc1_consumer_usage_t>(intUsage);
-        return callDescriptorFunction(device, descriptorId,
-                &Descriptor::setConsumerUsage, usage);
-    }
-
-    static int32_t setDimensionsHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptorId, uint32_t width,
-            uint32_t height) {
-        return callDescriptorFunction(device, descriptorId,
-                &Descriptor::setDimensions, width, height);
-    }
-
-    static int32_t setFormatHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptorId, int32_t format) {
-        return callDescriptorFunction(device, descriptorId,
-                &Descriptor::setFormat, format);
-    }
-
-    static int32_t setProducerUsageHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
-        auto usage = static_cast<gralloc1_producer_usage_t>(intUsage);
-        return callDescriptorFunction(device, descriptorId,
-                &Descriptor::setProducerUsage, usage);
-    }
-
-    // Buffer handle query functions
-
-    class Buffer {
-    public:
-        Buffer(buffer_handle_t handle, gralloc1_backing_store_t store,
-                const Descriptor& descriptor, uint32_t stride,
-                bool wasAllocated);
-
-        buffer_handle_t getHandle() const { return mHandle; }
-
-        void retain() { ++mReferenceCount; }
-
-        // Returns true if the reference count has dropped to 0, indicating that
-        // the buffer needs to be released
-        bool release() { return --mReferenceCount == 0; }
-
-        bool wasAllocated() const { return mWasAllocated; }
-
-        gralloc1_error_t getBackingStore(
-                gralloc1_backing_store_t* outStore) const {
-            *outStore = mStore;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getConsumerUsage(
-                gralloc1_consumer_usage_t* outUsage) const {
-            *outUsage = mDescriptor.consumerUsage;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getDimensions(uint32_t* outWidth,
-                uint32_t* outHeight) const {
-            *outWidth = mDescriptor.width;
-            *outHeight = mDescriptor.height;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getFormat(int32_t* outFormat) const {
-            *outFormat = mDescriptor.format;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const {
-            // TODO: This is conservative, and we could do better by examining
-            // the format, but it won't hurt anything for now
-            *outNumPlanes = 4;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getProducerUsage(
-                gralloc1_producer_usage_t* outUsage) const {
-            *outUsage = mDescriptor.producerUsage;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-        gralloc1_error_t getStride(uint32_t* outStride) const {
-            *outStride = mStride;
-            return GRALLOC1_ERROR_NONE;
-        }
-
-    private:
-
-        const buffer_handle_t mHandle;
-        size_t mReferenceCount;
-
-        // Since we're adapting to gralloc0, there will always be a 1:1
-        // correspondence between buffer handles and backing stores, and the
-        // backing store ID will be the same as the GraphicBuffer unique ID
-        const gralloc1_backing_store_t mStore;
-
-        const Descriptor mDescriptor;
-        const uint32_t mStride;
-
-        // Whether this buffer allocated in this process (as opposed to just
-        // being retained here), which determines whether to free or unregister
-        // the buffer when this Buffer is released
-        const bool mWasAllocated;
-    };
-
-    template <typename ...Args>
-    static int32_t callBufferFunction(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle,
-            gralloc1_error_t (Buffer::*member)(Args...) const, Args... args) {
-        auto buffer = getAdapter(device)->getBuffer(bufferHandle);
-        if (!buffer) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
-        }
-        auto error = ((*buffer).*member)(std::forward<Args>(args)...);
-        return static_cast<int32_t>(error);
-    }
-
-    template <typename MF, MF memFunc, typename ...Args>
-    static int32_t bufferHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle, Args... args) {
-        return Gralloc1On0Adapter::callBufferFunction(device, bufferHandle,
-                memFunc, std::forward<Args>(args)...);
-    }
-
-    static int32_t getConsumerUsageHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle, uint64_t* outUsage) {
-        auto usage = GRALLOC1_CONSUMER_USAGE_NONE;
-        auto error = callBufferFunction(device, bufferHandle,
-                &Buffer::getConsumerUsage, &usage);
-        if (error != GRALLOC1_ERROR_NONE) {
-            *outUsage = static_cast<uint64_t>(usage);
-        }
-        return error;
-    }
-
-    static int32_t getProducerUsageHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle, uint64_t* outUsage) {
-        auto usage = GRALLOC1_PRODUCER_USAGE_NONE;
-        auto error = callBufferFunction(device, bufferHandle,
-                &Buffer::getProducerUsage, &usage);
-        if (error != GRALLOC1_ERROR_NONE) {
-            *outUsage = static_cast<uint64_t>(usage);
-        }
-        return error;
-    }
-
-    // Buffer management functions
-
-    // We don't provide GRALLOC1_FUNCTION_ALLOCATE, since this should always be
-    // called through GRALLOC1_FUNCTION_ALLOCATE_WITH_ID
-    gralloc1_error_t allocate(
-            const std::shared_ptr<Descriptor>& descriptor,
-            gralloc1_backing_store_t id,
-            buffer_handle_t* outBufferHandle);
-    static gralloc1_error_t allocateWithIdHook(gralloc1_device_t* device,
-            gralloc1_buffer_descriptor_t descriptors,
-            gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-
-    gralloc1_error_t retain(const std::shared_ptr<Buffer>& buffer);
-    gralloc1_error_t release(const std::shared_ptr<Buffer>& buffer);
-
-    // Member function pointer 'member' will either be retain or release
-    template <gralloc1_error_t (Gralloc1On0Adapter::*member)(
-            const std::shared_ptr<Buffer>& buffer)>
-    static int32_t managementHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle) {
-        auto adapter = getAdapter(device);
-
-        auto buffer = adapter->getBuffer(bufferHandle);
-        if (!buffer) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
-        }
-
-        auto error = ((*adapter).*member)(buffer);
-        return static_cast<int32_t>(error);
-    }
-
-    gralloc1_error_t retain(const GraphicBuffer* buffer);
-    static gralloc1_error_t retainGraphicBufferHook(gralloc1_device_t* device,
-            const GraphicBuffer* buffer) {
-        auto adapter = getAdapter(device);
-        return adapter->retain(buffer);
-    }
-
-    // Buffer access functions
-
-    gralloc1_error_t lock(const std::shared_ptr<Buffer>& buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t& accessRegion, void** outData,
-            const sp<Fence>& acquireFence);
-    gralloc1_error_t lockFlex(const std::shared_ptr<Buffer>& buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t& accessRegion,
-            struct android_flex_layout* outFlex,
-            const sp<Fence>& acquireFence);
-    gralloc1_error_t lockYCbCr(const std::shared_ptr<Buffer>& buffer,
-            gralloc1_producer_usage_t producerUsage,
-            gralloc1_consumer_usage_t consumerUsage,
-            const gralloc1_rect_t& accessRegion,
-            struct android_ycbcr* outFlex,
-            const sp<Fence>& acquireFence);
-
-    template <typename OUT, gralloc1_error_t (Gralloc1On0Adapter::*member)(
-            const std::shared_ptr<Buffer>&, gralloc1_producer_usage_t,
-            gralloc1_consumer_usage_t, const gralloc1_rect_t&, OUT*,
-            const sp<Fence>&)>
-    static int32_t lockHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle,
-            uint64_t /*gralloc1_producer_usage_t*/ uintProducerUsage,
-            uint64_t /*gralloc1_consumer_usage_t*/ uintConsumerUsage,
-            const gralloc1_rect_t* accessRegion, OUT* outData,
-            int32_t acquireFenceFd) {
-        auto adapter = getAdapter(device);
-
-        // Exactly one of producer and consumer usage must be *_USAGE_NONE,
-        // but we can't check this until the upper levels of the framework
-        // correctly distinguish between producer and consumer usage
-        /*
-        bool hasProducerUsage =
-                uintProducerUsage != GRALLOC1_PRODUCER_USAGE_NONE;
-        bool hasConsumerUsage =
-                uintConsumerUsage != GRALLOC1_CONSUMER_USAGE_NONE;
-        if (hasProducerUsage && hasConsumerUsage ||
-                !hasProducerUsage && !hasConsumerUsage) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
-        }
-        */
-
-        auto producerUsage =
-                static_cast<gralloc1_producer_usage_t>(uintProducerUsage);
-        auto consumerUsage =
-                static_cast<gralloc1_consumer_usage_t>(uintConsumerUsage);
-
-        if (!outData) {
-            const auto producerCpuUsage = GRALLOC1_PRODUCER_USAGE_CPU_READ |
-                    GRALLOC1_PRODUCER_USAGE_CPU_WRITE;
-            if (producerUsage & producerCpuUsage != 0) {
-                return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
-            }
-            if (consumerUsage & GRALLOC1_CONSUMER_USAGE_CPU_READ != 0) {
-                return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
-            }
-        }
-
-        auto buffer = adapter->getBuffer(bufferHandle);
-        if (!buffer) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
-        }
-
-        if (!accessRegion) {
-            ALOGE("accessRegion is null");
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
-        }
-
-        sp<Fence> acquireFence{new Fence(acquireFenceFd)};
-        auto error = ((*adapter).*member)(buffer, producerUsage, consumerUsage,
-                *accessRegion, outData, acquireFence);
-        return static_cast<int32_t>(error);
-    }
-
-    gralloc1_error_t unlock(const std::shared_ptr<Buffer>& buffer,
-            sp<Fence>* outReleaseFence);
-    static int32_t unlockHook(gralloc1_device_t* device,
-            buffer_handle_t bufferHandle, int32_t* outReleaseFenceFd) {
-        auto adapter = getAdapter(device);
-
-        auto buffer = adapter->getBuffer(bufferHandle);
-        if (!buffer) {
-            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
-        }
-
-        sp<Fence> releaseFence = Fence::NO_FENCE;
-        auto error = adapter->unlock(buffer, &releaseFence);
-        if (error == GRALLOC1_ERROR_NONE) {
-            *outReleaseFenceFd = releaseFence->dup();
-        }
-        return static_cast<int32_t>(error);
-    }
-
-    // Adapter internals
-    const gralloc_module_t* mModule;
-    uint8_t mMinorVersion;
-    alloc_device_t* mDevice;
-
-    std::shared_ptr<Descriptor> getDescriptor(
-            gralloc1_buffer_descriptor_t descriptorId);
-    std::shared_ptr<Buffer> getBuffer(buffer_handle_t bufferHandle);
-
-    static std::atomic<gralloc1_buffer_descriptor_t> sNextBufferDescriptorId;
-    std::mutex mDescriptorMutex;
-    std::unordered_map<gralloc1_buffer_descriptor_t,
-            std::shared_ptr<Descriptor>> mDescriptors;
-    std::mutex mBufferMutex;
-    std::unordered_map<buffer_handle_t, std::shared_ptr<Buffer>> mBuffers;
-};
-
-} // namespace android
-
-#endif
diff --git a/include/ui/Gralloc2.h b/include/ui/Gralloc2.h
new file mode 100644
index 0000000..f826b92
--- /dev/null
+++ b/include/ui/Gralloc2.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRALLOC2_H
+#define ANDROID_UI_GRALLOC2_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <system/window.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::IAllocator;
+using hardware::graphics::common::V1_0::BufferUsage;
+using hardware::graphics::common::V1_0::PixelFormat;
+using hardware::graphics::mapper::V2_0::BufferDescriptor;
+using hardware::graphics::mapper::V2_0::Error;
+using hardware::graphics::mapper::V2_0::IMapper;
+using hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+// A wrapper to IMapper
+class Mapper {
+public:
+    Mapper();
+
+    Error createDescriptor(
+            const IMapper::BufferDescriptorInfo& descriptorInfo,
+            BufferDescriptor* outDescriptor) const;
+
+    // Import a buffer that is from another HAL, another process, or is
+    // cloned.
+    //
+    // The returned handle must be freed with freeBuffer.
+    Error importBuffer(const hardware::hidl_handle& rawHandle,
+            buffer_handle_t* outBufferHandle) const;
+
+    void freeBuffer(buffer_handle_t bufferHandle) const;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
+            const IMapper::Rect& accessRegion,
+            int acquireFence, void** outData) const;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
+            const IMapper::Rect& accessRegion,
+            int acquireFence, YCbCrLayout* outLayout) const;
+
+    // unlock returns a fence sync object (or -1) and the fence sync object is
+    // owned by the caller
+    int unlock(buffer_handle_t bufferHandle) const;
+
+private:
+    sp<IMapper> mMapper;
+};
+
+// A wrapper to IAllocator
+class Allocator {
+public:
+    // An allocator relies on a mapper, and that mapper must be alive at all
+    // time.
+    Allocator(const Mapper& mapper);
+
+    std::string dumpDebugInfo() const;
+
+    /*
+     * The returned buffers are already imported and must not be imported
+     * again.  outBufferHandles must point to a space that can contain at
+     * least "count" buffer_handle_t.
+     */
+    Error allocate(BufferDescriptor descriptor, uint32_t count,
+            uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+
+    Error allocate(BufferDescriptor descriptor,
+            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
+    {
+        return allocate(descriptor, 1, outStride, outBufferHandle);
+    }
+
+    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
+            uint32_t* outStride, buffer_handle_t* outBufferHandles) const
+    {
+        BufferDescriptor descriptor;
+        Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
+        if (error == Error::NONE) {
+            error = allocate(descriptor, count, outStride, outBufferHandles);
+        }
+        return error;
+    }
+
+    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
+    {
+        return allocate(descriptorInfo, 1, outStride, outBufferHandle);
+    }
+
+private:
+    const Mapper& mMapper;
+    sp<IAllocator> mAllocator;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC2_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3e127a1..4b82cff 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -20,13 +20,15 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <string>
+
 #include <ui/ANativeObjectBase.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 
-#include <string>
+#include <hardware/gralloc.h>
 
 struct ANativeWindowBuffer;
 
@@ -70,20 +72,69 @@
         USAGE_CURSOR            = GRALLOC_USAGE_CURSOR,
     };
 
+    static sp<GraphicBuffer> from(ANativeWindowBuffer *);
+
+
+    // Create a GraphicBuffer to be unflatten'ed into or be reallocated.
     GraphicBuffer();
 
-    // creates w * h buffer
+    // Create a GraphicBuffer by allocating and managing a buffer internally.
+    // This function is privileged.  See reallocate for details.
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, uint64_t inUsage,
+            std::string requestorName = "<Unknown>");
+
+    // Create a GraphicBuffer from an existing handle.
+    enum HandleWrapMethod : uint8_t {
+        // Wrap and use the handle directly.  It assumes the handle has been
+        // registered and never fails.  The handle must have a longer lifetime
+        // than this wrapping GraphicBuffer.
+        //
+        // This can be used when, for example, you want to wrap a handle that
+        // is already managed by another GraphicBuffer.
+        WRAP_HANDLE,
+
+        // Take ownership of the handle and use it directly.  It assumes the
+        // handle has been registered and never fails.
+        //
+        // This can be used to manage an already registered handle with
+        // GraphicBuffer.
+        TAKE_HANDLE,
+
+        // Take onwership of an unregistered handle and use it directly.  It
+        // can fail when the buffer does not register.  There is no ownership
+        // transfer on failures.
+        //
+        // This can be used to, for example, create a GraphicBuffer from a
+        // handle returned by Parcel::readNativeHandle.
+        TAKE_UNREGISTERED_HANDLE,
+
+        // Make a clone of the handle and use the cloned handle.  It can fail
+        // when cloning fails or when the buffer does not register.  There is
+        // never ownership transfer.
+        //
+        // This can be used to create a GraphicBuffer from a handle that
+        // cannot be used directly, such as one from hidl_handle.
+        CLONE_HANDLE,
+    };
+    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
+            uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t layerCount,
+            uint64_t usage, uint32_t stride);
+
+    // These functions are deprecated because they only take 32 bits of usage
+    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
+            uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t layerCount,
+            uint32_t usage, uint32_t stride)
+        : GraphicBuffer(handle, method, width, height, format, layerCount,
+                static_cast<uint64_t>(usage), stride) {}
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
+            native_handle_t* inHandle, bool keepOwnership);
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
-    // create a buffer from an existing handle
-    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
-            uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle,
-            bool keepOwnership);
-
-    // create a buffer from an existing ANativeWindowBuffer
-    GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
-
     // return status
     status_t initCheck() const;
 
@@ -92,6 +143,7 @@
     uint32_t getStride() const          { return static_cast<uint32_t>(stride); }
     uint32_t getUsage() const           { return static_cast<uint32_t>(usage); }
     PixelFormat getPixelFormat() const  { return format; }
+    uint32_t getLayerCount() const      { return static_cast<uint32_t>(layerCount); }
     Rect getBounds() const              { return Rect(width, height); }
     uint64_t getId() const              { return mId; }
 
@@ -100,11 +152,14 @@
         mGenerationNumber = generation;
     }
 
+    // This function is privileged.  It requires access to the allocator
+    // device or service, which usually involves adding suitable selinux
+    // rules.
     status_t reallocate(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
 
     bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
 
     status_t lock(uint32_t inUsage, void** vaddr);
     status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
@@ -116,6 +171,8 @@
     status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd);
     status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr,
             int fenceFd);
+    status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
+            const Rect& rect, void** vaddr, int fenceFd);
     status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr,
             int fenceFd);
     status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
@@ -159,18 +216,20 @@
     GraphicBuffer& operator = (const GraphicBuffer& rhs);
     const GraphicBuffer& operator = (const GraphicBuffer& rhs) const;
 
-    status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
-            uint32_t inUsage, std::string requestorName);
+    status_t initWithSize(uint32_t inWidth, uint32_t inHeight,
+            PixelFormat inFormat, uint32_t inLayerCount,
+            uint64_t inUsage, std::string requestorName);
+
+    status_t initWithHandle(const native_handle_t* handle,
+            HandleWrapMethod method, uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t layerCount,
+            uint64_t usage, uint32_t stride);
 
     void free_handle();
 
     GraphicBufferMapper& mBufferMapper;
     ssize_t mInitCheck;
 
-    // If we're wrapping another buffer then this reference will make sure it
-    // doesn't get freed.
-    sp<ANativeWindowBuffer> mWrappedBuffer;
-
     uint64_t mId;
 
     // Stores the generation number of this buffer. If this number does not
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..fe99de1 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -20,48 +20,38 @@
 
 #include <stdint.h>
 
+#include <memory>
+#include <string>
+
 #include <cutils/native_handle.h>
 
+#include <system/window.h>
+
+#include <ui/PixelFormat.h>
+
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 #include <utils/Singleton.h>
 
-#include <ui/Gralloc1.h>
-#include <ui/PixelFormat.h>
-
 namespace android {
 
-class Gralloc1Loader;
+namespace Gralloc2 {
+class Allocator;
+}
+
+class GraphicBufferMapper;
 class String8;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
 {
 public:
-    enum {
-        USAGE_SW_READ_NEVER     = GRALLOC1_CONSUMER_USAGE_CPU_READ_NEVER,
-        USAGE_SW_READ_RARELY    = GRALLOC1_CONSUMER_USAGE_CPU_READ,
-        USAGE_SW_READ_OFTEN     = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
-        USAGE_SW_READ_MASK      = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
-
-        USAGE_SW_WRITE_NEVER    = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_NEVER,
-        USAGE_SW_WRITE_RARELY   = GRALLOC1_PRODUCER_USAGE_CPU_WRITE,
-        USAGE_SW_WRITE_OFTEN    = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
-        USAGE_SW_WRITE_MASK     = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
-
-        USAGE_SOFTWARE_MASK     = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,
-
-        USAGE_HW_TEXTURE        = GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE,
-        USAGE_HW_RENDER         = GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
-        USAGE_HW_2D             = 0x00000400, // Deprecated
-        USAGE_HW_MASK           = 0x00071F00, // Deprecated
-    };
-
     static inline GraphicBufferAllocator& get() { return getInstance(); }
 
     status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
-            uint32_t usage, buffer_handle_t* handle, uint32_t* stride,
-            uint64_t graphicBufferId, std::string requestorName);
+            uint32_t layerCount, uint64_t usage,
+            buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+            std::string requestorName);
 
     status_t free(buffer_handle_t handle);
 
@@ -74,7 +64,8 @@
         uint32_t height;
         uint32_t stride;
         PixelFormat format;
-        uint32_t usage;
+        uint32_t layerCount;
+        uint64_t usage;
         size_t size;
         std::string requestorName;
     };
@@ -86,8 +77,8 @@
     GraphicBufferAllocator();
     ~GraphicBufferAllocator();
 
-    std::unique_ptr<Gralloc1::Loader> mLoader;
-    std::unique_ptr<Gralloc1::Device> mDevice;
+    GraphicBufferMapper& mMapper;
+    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h
index a25809c..e0702e9 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -20,14 +20,24 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <ui/Gralloc1.h>
+#include <memory>
 
 #include <utils/Singleton.h>
 
+
+// Needed by code that still uses the GRALLOC_USAGE_* constants.
+// when/if we get rid of gralloc, we should provide aliases or fix call sites.
+#include <hardware/gralloc.h>
+
+
 namespace android {
 
 // ---------------------------------------------------------------------------
 
+namespace Gralloc2 {
+class Mapper;
+}
+
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -35,10 +45,12 @@
 public:
     static inline GraphicBufferMapper& get() { return getInstance(); }
 
-    status_t registerBuffer(buffer_handle_t handle);
-    status_t registerBuffer(const GraphicBuffer* buffer);
+    // The imported outHandle must be freed with freeBuffer when no longer
+    // needed. rawHandle is owned by the caller.
+    status_t importBuffer(buffer_handle_t rawHandle,
+            buffer_handle_t* outHandle);
 
-    status_t unregisterBuffer(buffer_handle_t handle);
+    status_t freeBuffer(buffer_handle_t handle);
 
     status_t lock(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, void** vaddr);
@@ -51,19 +63,27 @@
     status_t lockAsync(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
 
+    status_t lockAsync(buffer_handle_t handle,
+            uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+            void** vaddr, int fenceFd);
+
     status_t lockAsyncYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
             int fenceFd);
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
+    const Gralloc2::Mapper& getGrallocMapper() const
+    {
+        return *mMapper;
+    }
+
 private:
     friend class Singleton<GraphicBufferMapper>;
 
     GraphicBufferMapper();
 
-    std::unique_ptr<Gralloc1::Loader> mLoader;
-    std::unique_ptr<Gralloc1::Device> mDevice;
+    const std::unique_ptr<const Gralloc2::Mapper> mMapper;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/include/ui/GraphicsEnv.h b/include/ui/GraphicsEnv.h
new file mode 100644
index 0000000..7817076
--- /dev/null
+++ b/include/ui/GraphicsEnv.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRAPHICS_ENV_H
+#define ANDROID_UI_GRAPHICS_ENV_H 1
+
+#include <string>
+
+struct android_namespace_t;
+
+namespace android {
+
+class GraphicsEnv {
+public:
+    static GraphicsEnv& getInstance();
+
+    // Set a search path for loading graphics drivers. The path is a list of
+    // directories separated by ':'. A directory can be contained in a zip file
+    // (drivers must be stored uncompressed and page aligned); such elements
+    // in the search path must have a '!' after the zip filename, e.g.
+    //     /data/app/com.example.driver/base.apk!/lib/arm64-v8a
+    void setDriverPath(const std::string path);
+    android_namespace_t* getDriverNamespace();
+
+private:
+    GraphicsEnv() = default;
+    std::string mDriverPath;
+    android_namespace_t* mDriverNamespace = nullptr;
+};
+
+} // namespace android
+
+/* FIXME
+ * Export an un-mangled function that just does
+ *     return android::GraphicsEnv::getInstance().getDriverNamespace();
+ * This allows libEGL to get the function pointer via dlsym, since it can't
+ * directly link against libgui. In a future release, we'll fix this so that
+ * libgui does not depend on graphics API libraries, and libEGL can link
+ * against it. The current dependencies from libgui -> libEGL are:
+ *  - the GLConsumer class, which should be moved to its own library
+ *  - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
+ *    will be removed soon.
+ */
+extern "C" android_namespace_t* android_getDriverNamespace();
+
+#endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/include/ui/HdrCapabilities.h b/include/ui/HdrCapabilities.h
index a7cd5fb..925aa1b 100644
--- a/include/ui/HdrCapabilities.h
+++ b/include/ui/HdrCapabilities.h
@@ -17,11 +17,15 @@
 #ifndef ANDROID_UI_HDR_CAPABILTIES_H
 #define ANDROID_UI_HDR_CAPABILTIES_H
 
-#include <binder/Parcelable.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include <utils/Flattenable.h>
 
 namespace android {
 
-class HdrCapabilities : public Parcelable
+class HdrCapabilities : public LightFlattenable<HdrCapabilities>
 {
 public:
     HdrCapabilities(const std::vector<int32_t /*android_hdr_t*/>& types,
@@ -32,8 +36,8 @@
         mMinLuminance(minLuminance) {}
 
     // Make this move-constructable and move-assignable
-    HdrCapabilities(HdrCapabilities&& other) = default;
-    HdrCapabilities& operator=(HdrCapabilities&& other) = default;
+    HdrCapabilities(HdrCapabilities&& other);
+    HdrCapabilities& operator=(HdrCapabilities&& other);
 
     HdrCapabilities()
       : mSupportedHdrTypes(),
@@ -41,7 +45,7 @@
         mMaxAverageLuminance(-1.0f),
         mMinLuminance(-1.0f) {}
 
-    virtual ~HdrCapabilities() = default;
+    ~HdrCapabilities();
 
     const std::vector<int32_t /*android_hdr_t*/>& getSupportedHdrTypes() const {
         return mSupportedHdrTypes;
@@ -50,9 +54,11 @@
     float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; }
     float getDesiredMinLuminance() const { return mMinLuminance; }
 
-    // Parcelable interface
-    virtual status_t writeToParcel(Parcel* parcel) const override;
-    virtual status_t readFromParcel(const Parcel* parcel) override;
+    // Flattenable protocol
+    bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
 
 private:
     std::vector<int32_t /*android_hdr_t*/> mSupportedHdrTypes;
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f26fecb..02773d9 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -53,13 +53,15 @@
 
     // real pixel formats supported for rendering -----------------------------
 
-    PIXEL_FORMAT_RGBA_8888   = HAL_PIXEL_FORMAT_RGBA_8888,   // 4x8-bit RGBA
-    PIXEL_FORMAT_RGBX_8888   = HAL_PIXEL_FORMAT_RGBX_8888,   // 4x8-bit RGB0
-    PIXEL_FORMAT_RGB_888     = HAL_PIXEL_FORMAT_RGB_888,     // 3x8-bit RGB
-    PIXEL_FORMAT_RGB_565     = HAL_PIXEL_FORMAT_RGB_565,     // 16-bit RGB
-    PIXEL_FORMAT_BGRA_8888   = HAL_PIXEL_FORMAT_BGRA_8888,   // 4x8-bit BGRA
-    PIXEL_FORMAT_RGBA_5551   = 6,                            // 16-bit ARGB
-    PIXEL_FORMAT_RGBA_4444   = 7,                            // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_8888    = HAL_PIXEL_FORMAT_RGBA_8888,    // 4x8-bit RGBA
+    PIXEL_FORMAT_RGBX_8888    = HAL_PIXEL_FORMAT_RGBX_8888,    // 4x8-bit RGB0
+    PIXEL_FORMAT_RGB_888      = HAL_PIXEL_FORMAT_RGB_888,      // 3x8-bit RGB
+    PIXEL_FORMAT_RGB_565      = HAL_PIXEL_FORMAT_RGB_565,      // 16-bit RGB
+    PIXEL_FORMAT_BGRA_8888    = HAL_PIXEL_FORMAT_BGRA_8888,    // 4x8-bit BGRA
+    PIXEL_FORMAT_RGBA_5551    = 6,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_4444    = 7,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_FP16    = HAL_PIXEL_FORMAT_RGBA_FP16,    // 64-bit RGBA
+    PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
 };
 
 typedef int32_t PixelFormat;
diff --git a/include/ui/Point.h b/include/ui/Point.h
index 1d7f64d..d050ede 100644
--- a/include/ui/Point.h
+++ b/include/ui/Point.h
@@ -34,7 +34,7 @@
     // Default constructor doesn't initialize the Point
     inline Point() {
     }
-    inline Point(int x, int y) : x(x), y(y) {
+    inline Point(int _x, int _y) : x(_x), y(_y) {
     }
 
     inline bool operator == (const Point& rhs) const {
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a8513a9..b50e4ec 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,6 +20,9 @@
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
+#include <log/log.h>
+
+#include <ui/FloatRect.h>
 #include <ui/Point.h>
 
 #include <android/rect.h>
@@ -42,13 +45,9 @@
     template <typename T>
     inline Rect(T w, T h) {
         if (w > INT32_MAX) {
-            ALOG(LOG_WARN, "Rect",
-                    "Width %u too large for Rect class, clamping", w);
             w = INT32_MAX;
         }
         if (h > INT32_MAX) {
-            ALOG(LOG_WARN, "Rect",
-                    "Height %u too large for Rect class, clamping", h);
             h = INT32_MAX;
         }
         left = top = 0;
@@ -177,11 +176,15 @@
     // this calculates (Region(*this) - exclude).bounds() efficiently
     Rect reduce(const Rect& exclude) const;
 
-
     // for backward compatibility
     inline int32_t width() const { return getWidth(); }
     inline int32_t height() const { return getHeight(); }
     inline void set(const Rect& rhs) { operator = (rhs); }
+
+    FloatRect toFloatRect() const {
+        return {static_cast<float>(left), static_cast<float>(top),
+                static_cast<float>(right), static_cast<float>(bottom)};
+    }
 };
 
 ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 810f098..7788452 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -147,21 +147,21 @@
     class rasterizer;
     friend class rasterizer;
 
-    Region& operationSelf(const Rect& r, int op);
-    Region& operationSelf(const Region& r, int op);
-    Region& operationSelf(const Region& r, int dx, int dy, int op);
-    const Region operation(const Rect& rhs, int op) const;
-    const Region operation(const Region& rhs, int op) const;
-    const Region operation(const Region& rhs, int dx, int dy, int op) const;
+    Region& operationSelf(const Rect& r, uint32_t op);
+    Region& operationSelf(const Region& r, uint32_t op);
+    Region& operationSelf(const Region& r, int dx, int dy, uint32_t op);
+    const Region operation(const Rect& rhs, uint32_t op) const;
+    const Region operation(const Region& rhs, uint32_t op) const;
+    const Region operation(const Region& rhs, int dx, int dy, uint32_t op) const;
 
-    static void boolean_operation(int op, Region& dst,
+    static void boolean_operation(uint32_t op, Region& dst,
             const Region& lhs, const Region& rhs, int dx, int dy);
-    static void boolean_operation(int op, Region& dst,
+    static void boolean_operation(uint32_t op, Region& dst,
             const Region& lhs, const Rect& rhs, int dx, int dy);
 
-    static void boolean_operation(int op, Region& dst,
+    static void boolean_operation(uint32_t op, Region& dst,
             const Region& lhs, const Region& rhs);
-    static void boolean_operation(int op, Region& dst,
+    static void boolean_operation(uint32_t op, Region& dst,
             const Region& lhs, const Rect& rhs);
 
     static void translate(Region& reg, int dx, int dy);
diff --git a/include/ui/TMatHelpers.h b/include/ui/TMatHelpers.h
deleted file mode 100644
index a6aadca..0000000
--- a/include/ui/TMatHelpers.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TMAT_IMPLEMENTATION
-#error "Don't include TMatHelpers.h directly. use ui/mat*.h instead"
-#else
-#undef TMAT_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TMAT_HELPERS_H
-#define UI_TMAT_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <math.h>
-#include <utils/Debug.h>
-#include <utils/String8.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/mat*.h
- */
-
-
-/*
- * Matrix utilities
- */
-
-namespace matrix {
-
-inline int     PURE transpose(int v)    { return v; }
-inline float   PURE transpose(float v)  { return v; }
-inline double  PURE transpose(double v) { return v; }
-
-inline int     PURE trace(int v)    { return v; }
-inline float   PURE trace(float v)  { return v; }
-inline double  PURE trace(double v) { return v; }
-
-template<typename MATRIX>
-MATRIX PURE inverse(const MATRIX& src) {
-
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::COL_SIZE == MATRIX::ROW_SIZE );
-
-    typename MATRIX::value_type t;
-    const size_t N = MATRIX::col_size();
-    size_t swap;
-    MATRIX tmp(src);
-    MATRIX inverse(1);
-
-    for (size_t i=0 ; i<N ; i++) {
-        // look for largest element in column
-        swap = i;
-        for (size_t j=i+1 ; j<N ; j++) {
-            if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
-                swap = j;
-            }
-        }
-
-        if (swap != i) {
-            /* swap rows. */
-            for (size_t k=0 ; k<N ; k++) {
-                t = tmp[i][k];
-                tmp[i][k] = tmp[swap][k];
-                tmp[swap][k] = t;
-
-                t = inverse[i][k];
-                inverse[i][k] = inverse[swap][k];
-                inverse[swap][k] = t;
-            }
-        }
-
-        t = 1 / tmp[i][i];
-        for (size_t k=0 ; k<N ; k++) {
-            tmp[i][k] *= t;
-            inverse[i][k] *= t;
-        }
-        for (size_t j=0 ; j<N ; j++) {
-            if (j != i) {
-                t = tmp[j][i];
-                for (size_t k=0 ; k<N ; k++) {
-                    tmp[j][k] -= tmp[i][k] * t;
-                    inverse[j][k] -= inverse[i][k] * t;
-                }
-            }
-        }
-    }
-    return inverse;
-}
-
-template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
-MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
-    // pre-requisite:
-    //  lhs : D columns, R rows
-    //  rhs : C columns, D rows
-    //  res : C columns, R rows
-
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_A::ROW_SIZE == MATRIX_B::COL_SIZE );
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::ROW_SIZE == MATRIX_B::ROW_SIZE );
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::COL_SIZE == MATRIX_A::COL_SIZE );
-
-    MATRIX_R res(MATRIX_R::NO_INIT);
-    for (size_t r=0 ; r<MATRIX_R::row_size() ; r++) {
-        res[r] = lhs * rhs[r];
-    }
-    return res;
-}
-
-// transpose. this handles matrices of matrices
-template <typename MATRIX>
-MATRIX PURE transpose(const MATRIX& m) {
-    // for now we only handle square matrix transpose
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    MATRIX result(MATRIX::NO_INIT);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        for (size_t c=0 ; c<MATRIX::col_size() ; c++)
-            result[c][r] = transpose(m[r][c]);
-    return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::value_type PURE trace(const MATRIX& m) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    typename MATRIX::value_type result(0);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        result += trace(m[r][r]);
-    return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::col_type PURE diag(const MATRIX& m) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        result[r] = m[r][r];
-    return result;
-}
-
-template <typename MATRIX>
-String8 asString(const MATRIX& m) {
-    String8 s;
-    for (size_t c=0 ; c<MATRIX::col_size() ; c++) {
-        s.append("|  ");
-        for (size_t r=0 ; r<MATRIX::row_size() ; r++) {
-            s.appendFormat("%7.2f  ", m[r][c]);
-        }
-        s.append("|\n");
-    }
-    return s;
-}
-
-}; // namespace matrix
-
-// -------------------------------------------------------------------------------------
-
-/*
- * TMatProductOperators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TMatProductOperators {
-public:
-    // multiply by a scalar
-    BASE<T>& operator *= (T v) {
-        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
-        for (size_t r=0 ; r<lhs.row_size() ; r++) {
-            lhs[r] *= v;
-        }
-        return lhs;
-    }
-
-    // divide by a scalar
-    BASE<T>& operator /= (T v) {
-        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
-        for (size_t r=0 ; r<lhs.row_size() ; r++) {
-            lhs[r] /= v;
-        }
-        return lhs;
-    }
-
-    // matrix * matrix, result is a matrix of the same type than the lhs matrix
-    template<typename U>
-    friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
-        return matrix::multiply<BASE<T> >(lhs, rhs);
-    }
-};
-
-
-/*
- * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
- *
- * BASE only needs to implement:
- *  - operator[]
- *  - col_type
- *  - row_type
- *  - COL_SIZE
- *  - ROW_SIZE
- *
- * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template<template<typename U> class BASE, typename T>
-class TMatSquareFunctions {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    friend BASE<T> PURE inverse(const BASE<T>& m)   { return matrix::inverse(m); }
-    friend BASE<T> PURE transpose(const BASE<T>& m) { return matrix::transpose(m); }
-    friend T       PURE trace(const BASE<T>& m)     { return matrix::trace(m); }
-};
-
-template <template<typename T> class BASE, typename T>
-class TMatDebug {
-public:
-    String8 asString() const {
-        return matrix::asString( static_cast< const BASE<T>& >(*this) );
-    }
-};
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_TMAT_HELPERS_H */
diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h
deleted file mode 100644
index bb7dbfc..0000000
--- a/include/ui/TVecHelpers.h
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TVEC_IMPLEMENTATION
-#error "Don't include TVecHelpers.h directly. use ui/vec*.h instead"
-#else
-#undef TVEC_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TVEC_HELPERS_H
-#define UI_TVEC_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/vec{2|3|4}.h
- */
-
-/*
- * This class casts itself into anything and assign itself from anything!
- * Use with caution!
- */
-template <typename TYPE>
-struct Impersonator {
-    Impersonator& operator = (const TYPE& rhs) {
-        reinterpret_cast<TYPE&>(*this) = rhs;
-        return *this;
-    }
-    operator TYPE& () {
-        return reinterpret_cast<TYPE&>(*this);
-    }
-    operator TYPE const& () const {
-        return reinterpret_cast<TYPE const&>(*this);
-    }
-};
-
-/*
- * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TVecAddOperators {
-public:
-    /* compound assignment from a another vector of the same size but different
-     * element type.
-     */
-    template <typename OTHER>
-    BASE<T>& operator += (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] += v[i];
-        }
-        return rhs;
-    }
-    template <typename OTHER>
-    BASE<T>& operator -= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] -= v[i];
-        }
-        return rhs;
-    }
-
-    /* compound assignment from a another vector of the same type.
-     * These operators can be used for implicit conversion and  handle operations
-     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
-     * to a vector (assuming the BASE<T> allows it).
-     */
-    BASE<T>& operator += (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] += v[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator -= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] -= v[i];
-        }
-        return rhs;
-    }
-
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-
-    /* The operators below handle operation between vectors of the same side
-     * but of a different element type.
-     */
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator +(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) += rv;
-    }
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) -= rv;
-    }
-
-    /* The operators below (which are not templates once this class is instanced,
-     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
-     * These handle operations like "vector * scalar" and "scalar * vector" by
-     * letting the compiler implicitly convert a scalar to a vector (assuming
-     * the BASE<T> allows it).
-     */
-    friend inline
-    BASE<T> PURE operator +(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) += rv;
-    }
-    friend inline
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) -= rv;
-    }
-};
-
-template <template<typename T> class BASE, typename T>
-class TVecProductOperators {
-public:
-    /* compound assignment from a another vector of the same size but different
-     * element type.
-     */
-    template <typename OTHER>
-    BASE<T>& operator *= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] *= v[i];
-        }
-        return rhs;
-    }
-    template <typename OTHER>
-    BASE<T>& operator /= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] /= v[i];
-        }
-        return rhs;
-    }
-
-    /* compound assignment from a another vector of the same type.
-     * These operators can be used for implicit conversion and  handle operations
-     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
-     * to a vector (assuming the BASE<T> allows it).
-     */
-    BASE<T>& operator *= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] *= v[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator /= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] /= v[i];
-        }
-        return rhs;
-    }
-
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-
-    /* The operators below handle operation between vectors of the same side
-     * but of a different element type.
-     */
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator *(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) *= rv;
-    }
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) /= rv;
-    }
-
-    /* The operators below (which are not templates once this class is instanced,
-     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
-     * These handle operations like "vector * scalar" and "scalar * vector" by
-     * letting the compiler implicitly convert a scalar to a vector (assuming
-     * the BASE<T> allows it).
-     */
-    friend inline
-    BASE<T> PURE operator *(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) *= rv;
-    }
-    friend inline
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) /= rv;
-    }
-};
-
-/*
- * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- *
- * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
- */
-template <template<typename T> class BASE, typename T>
-class TVecUnaryOperators {
-public:
-    BASE<T>& operator ++ () {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            ++rhs[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator -- () {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            --rhs[i];
-        }
-        return rhs;
-    }
-    BASE<T> operator - () const {
-        BASE<T> r(BASE<T>::NO_INIT);
-        BASE<T> const& rv(static_cast<BASE<T> const&>(*this));
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            r[i] = -rv[i];
-        }
-        return r;
-    }
-};
-
-
-/*
- * TVecComparisonOperators implements relational/comparison operators
- * on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecComparisonOperators {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    template<typename RT>
-    friend inline
-    bool PURE operator ==(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] != rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !operator ==(lv, rv);
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator >(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] <= rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !(lv > rv);
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator <(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] >= rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !(lv < rv);
-    }
-};
-
-
-/*
- * TVecFunctions implements functions on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecFunctions {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    template<typename RT>
-    friend inline
-    T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) {
-        T r(0);
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            r += lv[i]*rv[i];
-        return r;
-    }
-
-    friend inline
-    T PURE length(const BASE<T>& lv) {
-        return sqrt( dot(lv, lv) );
-    }
-
-    template<typename RT>
-    friend inline
-    T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) {
-        return length(rv - lv);
-    }
-
-    friend inline
-    BASE<T> PURE normalize(const BASE<T>& lv) {
-        return lv * (1 / length(lv));
-    }
-};
-
-#undef PURE
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-
-#endif /* UI_TVEC_HELPERS_H */
diff --git a/include/ui/mat4.h b/include/ui/mat4.h
deleted file mode 100644
index 4fd1eff..0000000
--- a/include/ui/mat4.h
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_MAT4_H
-#define UI_MAT4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec4.h>
-#include <utils/String8.h>
-
-#define TMAT_IMPLEMENTATION
-#include <ui/TMatHelpers.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tmat44 :  public TVecUnaryOperators<tmat44, T>,
-                public TVecComparisonOperators<tmat44, T>,
-                public TVecAddOperators<tmat44, T>,
-                public TMatProductOperators<tmat44, T>,
-                public TMatSquareFunctions<tmat44, T>,
-                public TMatDebug<tmat44, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-    typedef tvec4<T> col_type;
-    typedef tvec4<T> row_type;
-
-    // size of a column (i.e.: number of rows)
-    enum { COL_SIZE = col_type::SIZE };
-    static inline size_t col_size() { return COL_SIZE; }
-
-    // size of a row (i.e.: number of columns)
-    enum { ROW_SIZE = row_type::SIZE };
-    static inline size_t row_size() { return ROW_SIZE; }
-    static inline size_t size()     { return row_size(); }  // for TVec*<>
-
-private:
-
-    /*
-     *  <--  N columns  -->
-     *
-     *  a00 a10 a20 ... aN0    ^
-     *  a01 a11 a21 ... aN1    |
-     *  a02 a12 a22 ... aN2  M rows
-     *  ...                    |
-     *  a0M a1M a2M ... aNM    v
-     *
-     *  COL_SIZE = M
-     *  ROW_SIZE = N
-     *  m[0] = [a00 a01 a02 ... a01M]
-     */
-
-    col_type mValue[ROW_SIZE];
-
-public:
-    // array access
-    inline col_type const& operator [] (size_t i) const { return mValue[i]; }
-    inline col_type&       operator [] (size_t i)       { return mValue[i]; }
-
-    T const* asArray() const { return &mValue[0][0]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    /*
-     *  constructors
-     */
-
-    // leaves object uninitialized. use with caution.
-    explicit tmat44(no_init) { }
-
-    // initialize to identity
-    tmat44();
-
-    // initialize to Identity*scalar.
-    template<typename U>
-    explicit tmat44(U v);
-
-    // sets the diagonal to the passed vector
-    template <typename U>
-    explicit tmat44(const tvec4<U>& rhs);
-
-    // construct from another matrix of the same size
-    template <typename U>
-    explicit tmat44(const tmat44<U>& rhs);
-
-    // construct from 4 column vectors
-    template <typename A, typename B, typename C, typename D>
-    tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3);
-
-    // construct from 16 scalars
-    template <
-        typename A, typename B, typename C, typename D,
-        typename E, typename F, typename G, typename H,
-        typename I, typename J, typename K, typename L,
-        typename M, typename N, typename O, typename P>
-    tmat44( A m00, B m01, C m02, D m03,
-            E m10, F m11, G m12, H m13,
-            I m20, J m21, K m22, L m23,
-            M m30, N m31, O m32, P m33);
-
-    // construct from a C array
-    template <typename U>
-    explicit tmat44(U const* rawArray);
-
-    /*
-     *  helpers
-     */
-
-    static tmat44 ortho(T left, T right, T bottom, T top, T near, T far);
-
-    static tmat44 frustum(T left, T right, T bottom, T top, T near, T far);
-
-    template <typename A, typename B, typename C>
-    static tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up);
-
-    template <typename A>
-    static tmat44 translate(const tvec4<A>& t);
-
-    template <typename A>
-    static tmat44 scale(const tvec4<A>& s);
-
-    template <typename A, typename B>
-    static tmat44 rotate(A radian, const tvec3<B>& about);
-};
-
-// ----------------------------------------------------------------------------------------
-// Constructors
-// ----------------------------------------------------------------------------------------
-
-/*
- * Since the matrix code could become pretty big quickly, we don't inline most
- * operations.
- */
-
-template <typename T>
-tmat44<T>::tmat44() {
-    mValue[0] = col_type(1,0,0,0);
-    mValue[1] = col_type(0,1,0,0);
-    mValue[2] = col_type(0,0,1,0);
-    mValue[3] = col_type(0,0,0,1);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U v) {
-    mValue[0] = col_type(v,0,0,0);
-    mValue[1] = col_type(0,v,0,0);
-    mValue[2] = col_type(0,0,v,0);
-    mValue[3] = col_type(0,0,0,v);
-}
-
-template<typename T>
-template<typename U>
-tmat44<T>::tmat44(const tvec4<U>& v) {
-    mValue[0] = col_type(v.x,0,0,0);
-    mValue[1] = col_type(0,v.y,0,0);
-    mValue[2] = col_type(0,0,v.z,0);
-    mValue[3] = col_type(0,0,0,v.w);
-}
-
-// construct from 16 scalars
-template<typename T>
-template <
-    typename A, typename B, typename C, typename D,
-    typename E, typename F, typename G, typename H,
-    typename I, typename J, typename K, typename L,
-    typename M, typename N, typename O, typename P>
-tmat44<T>::tmat44(  A m00, B m01, C m02, D m03,
-                    E m10, F m11, G m12, H m13,
-                    I m20, J m21, K m22, L m23,
-                    M m30, N m31, O m32, P m33) {
-    mValue[0] = col_type(m00, m01, m02, m03);
-    mValue[1] = col_type(m10, m11, m12, m13);
-    mValue[2] = col_type(m20, m21, m22, m23);
-    mValue[3] = col_type(m30, m31, m32, m33);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(const tmat44<U>& rhs) {
-    for (size_t r=0 ; r<row_size() ; r++)
-        mValue[r] = rhs[r];
-}
-
-template <typename T>
-template <typename A, typename B, typename C, typename D>
-tmat44<T>::tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3) {
-    mValue[0] = v0;
-    mValue[1] = v1;
-    mValue[2] = v2;
-    mValue[3] = v3;
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U const* rawArray) {
-    for (size_t r=0 ; r<row_size() ; r++)
-        for (size_t c=0 ; c<col_size() ; c++)
-            mValue[r][c] = *rawArray++;
-}
-
-// ----------------------------------------------------------------------------------------
-// Helpers
-// ----------------------------------------------------------------------------------------
-
-template <typename T>
-tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
-    tmat44<T> m;
-    m[0][0] =  2 / (right - left);
-    m[1][1] =  2 / (top   - bottom);
-    m[2][2] = -2 / (far   - near);
-    m[3][0] = -(right + left)   / (right - left);
-    m[3][1] = -(top   + bottom) / (top   - bottom);
-    m[3][2] = -(far   + near)   / (far   - near);
-    return m;
-}
-
-template <typename T>
-tmat44<T> tmat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
-    tmat44<T> m;
-    T A = (right + left)   / (right - left);
-    T B = (top   + bottom) / (top   - bottom);
-    T C = (far   + near)   / (far   - near);
-    T D = (2 * far * near) / (far   - near);
-    m[0][0] = (2 * near) / (right - left);
-    m[1][1] = (2 * near) / (top   - bottom);
-    m[2][0] = A;
-    m[2][1] = B;
-    m[2][2] = C;
-    m[2][3] =-1;
-    m[3][2] = D;
-    m[3][3] = 0;
-    return m;
-}
-
-template <typename T>
-template <typename A, typename B, typename C>
-tmat44<T> tmat44<T>::lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up) {
-    tvec3<T> L(normalize(center - eye));
-    tvec3<T> S(normalize( cross(L, up) ));
-    tvec3<T> U(cross(S, L));
-    return tmat44<T>(
-            tvec4<T>( S, 0),
-            tvec4<T>( U, 0),
-            tvec4<T>(-L, 0),
-            tvec4<T>(-eye, 1));
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::translate(const tvec4<A>& t) {
-    tmat44<T> r;
-    r[3] = t;
-    return r;
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::scale(const tvec4<A>& s) {
-    tmat44<T> r;
-    r[0][0] = s[0];
-    r[1][1] = s[1];
-    r[2][2] = s[2];
-    r[3][3] = s[3];
-    return r;
-}
-
-template <typename T>
-template <typename A, typename B>
-tmat44<T> tmat44<T>::rotate(A radian, const tvec3<B>& about) {
-    tmat44<T> rotation;
-    T* r = const_cast<T*>(rotation.asArray());
-    T c = cos(radian);
-    T s = sin(radian);
-    if (about.x==1 && about.y==0 && about.z==0) {
-        r[5] = c;   r[10]= c;
-        r[6] = s;   r[9] = -s;
-    } else if (about.x==0 && about.y==1 && about.z==0) {
-        r[0] = c;   r[10]= c;
-        r[8] = s;   r[2] = -s;
-    } else if (about.x==0 && about.y==0 && about.z==1) {
-        r[0] = c;   r[5] = c;
-        r[1] = s;   r[4] = -s;
-    } else {
-        tvec3<B> nabout = normalize(about);
-        B x = nabout.x;
-        B y = nabout.y;
-        B z = nabout.z;
-        T nc = 1 - c;
-        T xy = x * y;
-        T yz = y * z;
-        T zx = z * x;
-        T xs = x * s;
-        T ys = y * s;
-        T zs = z * s;
-        r[ 0] = x*x*nc +  c;    r[ 4] =  xy*nc - zs;    r[ 8] =  zx*nc + ys;
-        r[ 1] =  xy*nc + zs;    r[ 5] = y*y*nc +  c;    r[ 9] =  yz*nc - xs;
-        r[ 2] =  zx*nc - ys;    r[ 6] =  yz*nc + xs;    r[10] = z*z*nc +  c;
-    }
-    return rotation;
-}
-
-// ----------------------------------------------------------------------------------------
-// Arithmetic operators outside of class
-// ----------------------------------------------------------------------------------------
-
-/* We use non-friend functions here to prevent the compiler from using
- * implicit conversions, for instance of a scalar to a vector. The result would
- * not be what the caller expects.
- *
- * Also note that the order of the arguments in the inner loop is important since
- * it determines the output type (only relevant when T != U).
- */
-
-// matrix * vector, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::col_type PURE operator *(const tmat44<T>& lv, const tvec4<U>& rv) {
-    typename tmat44<U>::col_type result;
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result += rv[r]*lv[r];
-    return result;
-}
-
-// vector * matrix, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::row_type PURE operator *(const tvec4<U>& rv, const tmat44<T>& lv) {
-    typename tmat44<U>::row_type result(tmat44<U>::row_type::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = dot(rv, lv[r]);
-    return result;
-}
-
-// matrix * scalar, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(const tmat44<T>& lv, U rv) {
-    tmat44<T> result(tmat44<T>::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = lv[r]*rv;
-    return result;
-}
-
-// scalar * matrix, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(U rv, const tmat44<T>& lv) {
-    tmat44<T> result(tmat44<T>::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = lv[r]*rv;
-    return result;
-}
-
-// ----------------------------------------------------------------------------------------
-
-/* FIXME: this should go into TMatSquareFunctions<> but for some reason
- * BASE<T>::col_type is not accessible from there (???)
- */
-template<typename T>
-typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) {
-    return matrix::diag(m);
-}
-
-// ----------------------------------------------------------------------------------------
-
-typedef tmat44<float> mat4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_MAT4_H */
diff --git a/include/ui/vec2.h b/include/ui/vec2.h
deleted file mode 100644
index c31d0e4..0000000
--- a/include/ui/vec2.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC2_H
-#define UI_VEC2_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define TVEC_IMPLEMENTATION
-#include <ui/TVecHelpers.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec2 :   public TVecProductOperators<tvec2, T>,
-                public TVecAddOperators<tvec2, T>,
-                public TVecUnaryOperators<tvec2, T>,
-                public TVecComparisonOperators<tvec2, T>,
-                public TVecFunctions<tvec2, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y; };
-        struct { T s, t; };
-        struct { T r, g; };
-    };
-
-    enum { SIZE = 2 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-
-    // leaves object uninitialized. use with caution.
-    explicit tvec2(no_init) { }
-
-    // default constructor
-    tvec2() : x(0), y(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec2(A v) : x(v), y(v) { }
-
-    template<typename A, typename B>
-    tvec2(A x, B y) : x(x), y(y) { }
-
-    template<typename A>
-    explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { }
-
-    template<typename A>
-    tvec2(const Impersonator< tvec2<A> >& v)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec2<float> vec2;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec3.h b/include/ui/vec3.h
deleted file mode 100644
index dde59a9..0000000
--- a/include/ui/vec3.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC3_H
-#define UI_VEC3_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec2.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec3 :   public TVecProductOperators<tvec3, T>,
-                public TVecAddOperators<tvec3, T>,
-                public TVecUnaryOperators<tvec3, T>,
-                public TVecComparisonOperators<tvec3, T>,
-                public TVecFunctions<tvec3, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y, z; };
-        struct { T s, t, p; };
-        struct { T r, g, b; };
-        Impersonator< tvec2<T> > xy;
-        Impersonator< tvec2<T> > st;
-        Impersonator< tvec2<T> > rg;
-    };
-
-    enum { SIZE = 3 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-    // leaves object uninitialized. use with caution.
-    explicit tvec3(no_init) { }
-
-    // default constructor
-    tvec3() : x(0), y(0), z(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec3(A v) : x(v), y(v), z(v) { }
-
-    template<typename A, typename B, typename C>
-    tvec3(A x, B y, C z) : x(x), y(y), z(z) { }
-
-    template<typename A, typename B>
-    tvec3(const tvec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
-
-    template<typename A>
-    explicit tvec3(const tvec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
-
-    template<typename A>
-    tvec3(const Impersonator< tvec3<A> >& v)
-        : x(((const tvec3<A>&)v).x),
-          y(((const tvec3<A>&)v).y),
-          z(((const tvec3<A>&)v).z) { }
-
-    template<typename A, typename B>
-    tvec3(const Impersonator< tvec2<A> >& v, B z)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y),
-          z(z) { }
-
-    // cross product works only on vectors of size 3
-    template <typename RT>
-    friend inline
-    tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) {
-        return tvec3(
-                u.y*v.z - u.z*v.y,
-                u.z*v.x - u.x*v.z,
-                u.x*v.y - u.y*v.x);
-    }
-};
-
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec3<float> vec3;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec4.h b/include/ui/vec4.h
deleted file mode 100644
index e03d331..0000000
--- a/include/ui/vec4.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC4_H
-#define UI_VEC4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec3.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec4 :   public TVecProductOperators<tvec4, T>,
-                public TVecAddOperators<tvec4, T>,
-                public TVecUnaryOperators<tvec4, T>,
-                public TVecComparisonOperators<tvec4, T>,
-                public TVecFunctions<tvec4, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y, z, w; };
-        struct { T s, t, p, q; };
-        struct { T r, g, b, a; };
-        Impersonator< tvec2<T> > xy;
-        Impersonator< tvec2<T> > st;
-        Impersonator< tvec2<T> > rg;
-        Impersonator< tvec3<T> > xyz;
-        Impersonator< tvec3<T> > stp;
-        Impersonator< tvec3<T> > rgb;
-    };
-
-    enum { SIZE = 4 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-
-    // leaves object uninitialized. use with caution.
-    explicit tvec4(no_init) { }
-
-    // default constructor
-    tvec4() : x(0), y(0), z(0), w(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec4(A v) : x(v), y(v), z(v), w(v) { }
-
-    template<typename A, typename B, typename C, typename D>
-    tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
-
-    template<typename A, typename B, typename C>
-    tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
-
-    template<typename A, typename B>
-    tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
-
-    template<typename A>
-    explicit tvec4(const tvec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
-
-    template<typename A>
-    tvec4(const Impersonator< tvec4<A> >& v)
-        : x(((const tvec4<A>&)v).x),
-          y(((const tvec4<A>&)v).y),
-          z(((const tvec4<A>&)v).z),
-          w(((const tvec4<A>&)v).w) { }
-
-    template<typename A, typename B>
-    tvec4(const Impersonator< tvec3<A> >& v, B w)
-        : x(((const tvec3<A>&)v).x),
-          y(((const tvec3<A>&)v).y),
-          z(((const tvec3<A>&)v).z),
-          w(w) { }
-
-    template<typename A, typename B, typename C>
-    tvec4(const Impersonator< tvec2<A> >& v, B z, C w)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y),
-          z(z),
-          w(w) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec4<float> vec4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/vr/vr_manager/vr_manager.h b/include/vr/vr_manager/vr_manager.h
new file mode 100644
index 0000000..9df2c6b
--- /dev/null
+++ b/include/vr/vr_manager/vr_manager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VR_MANAGER_H
+#define ANDROID_VR_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class IVrStateCallbacks : public IInterface {
+public:
+    DECLARE_META_INTERFACE(VrStateCallbacks)
+
+    virtual void onVrStateChanged(bool enabled) = 0;
+};
+
+enum VrStateCallbacksTransaction {
+    ON_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BnVrStateCallbacks : public BnInterface<IVrStateCallbacks> {
+public:
+    status_t onTransact(uint32_t code, const Parcel& data,
+                        Parcel* reply, uint32_t flags = 0) override;
+};
+
+
+// Must be kept in sync with interface defined in
+// IPersistentVrStateCallbacks.aidl.
+
+class IPersistentVrStateCallbacks : public IInterface {
+public:
+    DECLARE_META_INTERFACE(PersistentVrStateCallbacks)
+
+    virtual void onPersistentVrStateChanged(bool enabled) = 0;
+};
+
+enum PersistentVrStateCallbacksTransaction {
+    ON_PERSISTENT_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BnPersistentVrStateCallbacks
+        : public BnInterface<IPersistentVrStateCallbacks> {
+public:
+    status_t onTransact(uint32_t code, const Parcel& data,
+                        Parcel* reply, uint32_t flags = 0) override;
+};
+
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class IVrManager : public IInterface {
+public:
+    DECLARE_META_INTERFACE(VrManager)
+
+    virtual void registerListener(const sp<IVrStateCallbacks>& cb) = 0;
+    virtual void unregisterListener(const sp<IVrStateCallbacks>& cb) = 0;
+    virtual void registerPersistentVrStateListener(
+        const sp<IPersistentVrStateCallbacks>& cb) = 0;
+    virtual void unregisterPersistentVrStateListener(
+        const sp<IPersistentVrStateCallbacks>& cb) = 0;
+    virtual bool getVrModeState() = 0;
+};
+
+enum VrManagerTransaction {
+    REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
+    UNREGISTER_LISTENER,
+    REGISTER_PERSISTENT_VR_STATE_LISTENER,
+    UNREGISTER_PERSISTENT_VR_STATE_LISTENER,
+    GET_VR_MODE_STATE,
+};
+
+};  // namespace android
+
+#endif // ANDROID_VR_MANAGER_H
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
new file mode 100644
index 0000000..0d25176
--- /dev/null
+++ b/libs/arect/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libarect_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
+cc_library_static {
+    name: "libarect",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
diff --git a/libs/arect/MODULE_LICENSE_APACHE2 b/libs/arect/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/arect/MODULE_LICENSE_APACHE2
diff --git a/libs/arect/NOTICE b/libs/arect/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/arect/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/include/android/rect.h b/libs/arect/include/android/rect.h
similarity index 100%
rename from include/android/rect.h
rename to libs/arect/include/android/rect.h
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
new file mode 100644
index 0000000..204fdb5
--- /dev/null
+++ b/libs/binder/Android.bp
@@ -0,0 +1,92 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+    name: "libbinder_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libbinder",
+
+    // for vndbinder
+    vendor_available: true,
+
+    srcs: [
+        "AppOpsManager.cpp",
+        "Binder.cpp",
+        "BpBinder.cpp",
+        "BufferedTextOutput.cpp",
+        "Debug.cpp",
+        "IActivityManager.cpp",
+        "IAppOpsCallback.cpp",
+        "IAppOpsService.cpp",
+        "IBatteryStats.cpp",
+        "IInterface.cpp",
+        "IMediaResourceMonitor.cpp",
+        "IMemory.cpp",
+        "IPCThreadState.cpp",
+        "IPermissionController.cpp",
+        "IProcessInfoService.cpp",
+        "IResultReceiver.cpp",
+        "IServiceManager.cpp",
+        "IShellCallback.cpp",
+        "MemoryBase.cpp",
+        "MemoryDealer.cpp",
+        "MemoryHeapBase.cpp",
+        "Parcel.cpp",
+        "PermissionCache.cpp",
+        "PersistableBundle.cpp",
+        "ProcessInfoService.cpp",
+        "ProcessState.cpp",
+        "Static.cpp",
+        "Status.cpp",
+        "TextOutput.cpp",
+        "IpPrefix.cpp",
+        "Value.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    product_variables: {
+        binder32bit: {
+            cflags: ["-DBINDER_IPC_32BIT=1"],
+        },
+    },
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libcutils",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libbase",
+        "libutils",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    clang: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+}
+
+subdirs = ["tests"]
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
deleted file mode 100644
index 14be920..0000000
--- a/libs/binder/Android.mk
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# we have the common sources, plus some device-specific stuff
-sources := \
-    AppOpsManager.cpp \
-    Binder.cpp \
-    BpBinder.cpp \
-    BufferedTextOutput.cpp \
-    Debug.cpp \
-    IAppOpsCallback.cpp \
-    IAppOpsService.cpp \
-    IBatteryStats.cpp \
-    IInterface.cpp \
-    IMediaResourceMonitor.cpp \
-    IMemory.cpp \
-    IPCThreadState.cpp \
-    IPermissionController.cpp \
-    IProcessInfoService.cpp \
-    IResultReceiver.cpp \
-    IServiceManager.cpp \
-    MemoryBase.cpp \
-    MemoryDealer.cpp \
-    MemoryHeapBase.cpp \
-    Parcel.cpp \
-    PermissionCache.cpp \
-    PersistableBundle.cpp \
-    ProcessInfoService.cpp \
-    ProcessState.cpp \
-    Static.cpp \
-    Status.cpp \
-    TextOutput.cpp \
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinder
-LOCAL_SHARED_LIBRARIES := liblog libcutils libutils
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES := $(sources)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-LOCAL_CFLAGS += -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinder
-LOCAL_STATIC_LIBRARIES += libutils
-LOCAL_SRC_FILES := $(sources)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-LOCAL_CFLAGS += -Werror
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 9a061a0..f3b86ae 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <mutex>
 #include <binder/AppOpsManager.h>
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
@@ -22,6 +23,19 @@
 
 namespace android {
 
+namespace {
+
+#if defined(__BRILLO__)
+// Because Brillo has no application model, security policy is managed
+// statically (at build time) with SELinux controls.
+// As a consequence, it also never runs the AppOpsManager service.
+const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED;
+#else
+const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
+#endif  // defined(__BRILLO__)
+
+}  // namespace
+
 static String16 _appops("appops");
 static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
 static sp<IBinder> gToken;
@@ -39,10 +53,15 @@
 {
 }
 
+#if defined(__BRILLO__)
+// There is no AppOpsService on Brillo
+sp<IAppOpsService> AppOpsManager::getService() { return NULL; }
+#else
 sp<IAppOpsService> AppOpsManager::getService()
 {
+
+    std::lock_guard<Mutex> scoped_lock(mLock);
     int64_t startTime = 0;
-    mLock.lock();
     sp<IAppOpsService> service = mService;
     while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) {
         sp<IBinder> binder = defaultServiceManager()->checkService(_appops);
@@ -53,7 +72,8 @@
                 ALOGI("Waiting for app ops service");
             } else if ((uptimeMillis()-startTime) > 10000) {
                 ALOGW("Waiting too long for app ops service, giving up");
-                return NULL;
+                service = NULL;
+                break;
             }
             sleep(1);
         } else {
@@ -61,25 +81,30 @@
             mService = service;
         }
     }
-    mLock.unlock();
     return service;
 }
+#endif  // defined(__BRILLO__)
 
 int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
 {
     sp<IAppOpsService> service = getService();
-    return service != NULL ? service->checkOperation(op, uid, callingPackage) : MODE_IGNORED;
+    return service != NULL
+            ? service->checkOperation(op, uid, callingPackage)
+            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
     sp<IAppOpsService> service = getService();
-    return service != NULL ? service->noteOperation(op, uid, callingPackage) : MODE_IGNORED;
+    return service != NULL
+            ? service->noteOperation(op, uid, callingPackage)
+            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 }
 
 int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) {
     sp<IAppOpsService> service = getService();
-    return service != NULL ? service->startOperation(getToken(service), op, uid, callingPackage)
-            : MODE_IGNORED;
+    return service != NULL
+            ? service->startOperation(getToken(service), op, uid, callingPackage)
+            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c4d47ca..890ef30 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -21,6 +21,7 @@
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
 #include <binder/IResultReceiver.h>
+#include <binder/IShellCallback.h>
 #include <binder/Parcel.h>
 
 #include <stdio.h>
@@ -62,7 +63,8 @@
 
 
 status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
-    Vector<String16>& args, const sp<IResultReceiver>& resultReceiver)
+    Vector<String16>& args, const sp<IShellCallback>& callback,
+    const sp<IResultReceiver>& resultReceiver)
 {
     Parcel send;
     Parcel reply;
@@ -74,6 +76,7 @@
     for (size_t i = 0; i < numArgs; i++) {
         send.writeString16(args[i]);
     }
+    send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL);
     send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL);
     return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
 }
@@ -232,11 +235,17 @@
             for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
                args.add(data.readString16());
             }
+            sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+                    data.readStrongBinder());
             sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
                     data.readStrongBinder());
 
             // XXX can't add virtuals until binaries are updated.
             //return shellCommand(in, out, err, args, resultReceiver);
+            (void)in;
+            (void)out;
+            (void)err;
+
             if (resultReceiver != NULL) {
                 resultReceiver->send(INVALID_OPERATION);
             }
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 1339a67..a2443c0 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -34,7 +34,7 @@
 
 struct BufferedTextOutput::BufferState : public RefBase
 {
-    BufferState(int32_t _seq)
+    explicit BufferState(int32_t _seq)
         : seq(_seq)
         , buffer(NULL)
         , bufferPos(0)
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
new file mode 100644
index 0000000..50a8b28
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IActivityManager.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpActivityManager : public BpInterface<IActivityManager>
+{
+public:
+    explicit BpActivityManager(const sp<IBinder>& impl)
+        : BpInterface<IActivityManager>(impl)
+    {
+    }
+
+    virtual int openContentUri(const String16& stringUri)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeString16(stringUri);
+        status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply);
+        int fd = -1;
+        if (ret == NO_ERROR) {
+            int32_t exceptionCode = reply.readExceptionCode();
+            if (!exceptionCode) {
+                // Success is indicated here by a nonzero int followed by the fd;
+                // failure by a zero int with no data following.
+                if (reply.readInt32() != 0) {
+                    fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+                }
+            } else {
+                // An exception was thrown back; fall through to return failure
+                ALOGD("openContentUri(%s) caught exception %d\n",
+                        String8(stringUri).string(), exceptionCode);
+            }
+        }
+        return fd;
+    }
+};
+
+// ------------------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+
+}; // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 2aaf566..f9ec593 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -31,7 +31,7 @@
 class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
 {
 public:
-    BpAppOpsCallback(const sp<IBinder>& impl)
+    explicit BpAppOpsCallback(const sp<IBinder>& impl)
         : BpInterface<IAppOpsCallback>(impl)
     {
     }
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 9558376..638ae5c 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -31,7 +31,7 @@
 class BpAppOpsService : public BpInterface<IAppOpsService>
 {
 public:
-    BpAppOpsService(const sp<IBinder>& impl)
+    explicit BpAppOpsService(const sp<IBinder>& impl)
         : BpInterface<IAppOpsService>(impl)
     {
     }
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index e32c628..ad1e69f 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -29,7 +29,7 @@
 class BpBatteryStats : public BpInterface<IBatteryStats>
 {
 public:
-    BpBatteryStats(const sp<IBinder>& impl)
+    explicit BpBatteryStats(const sp<IBinder>& impl)
         : BpInterface<IBatteryStats>(impl)
     {
     }
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4800f5b..77e3d23 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -25,7 +25,7 @@
 
 class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
 public:
-    BpMediaResourceMonitor(const sp<IBinder>& impl)
+    explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
         : BpInterface<IMediaResourceMonitor>(impl) {}
 
     virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 5f345cf..5c1a4f4 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -16,22 +16,24 @@
 
 #define LOG_TAG "IMemory"
 
+#include <atomic>
+#include <stdatomic.h>
+
+#include <fcntl.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include <binder/IMemory.h>
-#include <cutils/log.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+
+#include <utils/CallStack.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
-#include <utils/Atomic.h>
-#include <binder/Parcel.h>
-#include <utils/CallStack.h>
 
 #define VERBOSE   0
 
@@ -56,12 +58,15 @@
     struct heap_info_t {
         sp<IMemoryHeap> heap;
         int32_t         count;
+        // Note that this cannot be meaningfully copied.
     };
 
     void free_heap(const wp<IBinder>& binder);
 
-    Mutex mHeapCacheLock;
+    Mutex mHeapCacheLock;  // Protects entire vector below.
     KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+    // We do not use the copy-on-write capabilities of KeyedVector.
+    // TODO: Reimplemement based on standard C++ container?
 };
 
 static sp<HeapCache> gHeapCache = new HeapCache();
@@ -75,7 +80,7 @@
 class BpMemoryHeap : public BpInterface<IMemoryHeap>
 {
 public:
-    BpMemoryHeap(const sp<IBinder>& impl);
+    explicit BpMemoryHeap(const sp<IBinder>& impl);
     virtual ~BpMemoryHeap();
 
     virtual int getHeapID() const;
@@ -105,7 +110,7 @@
     void assertMapped() const;
     void assertReallyMapped() const;
 
-    mutable volatile int32_t mHeapId;
+    mutable std::atomic<int32_t> mHeapId;
     mutable void*       mBase;
     mutable size_t      mSize;
     mutable uint32_t    mFlags;
@@ -123,7 +128,7 @@
 class BpMemory : public BpInterface<IMemory>
 {
 public:
-    BpMemory(const sp<IBinder>& impl);
+    explicit BpMemory(const sp<IBinder>& impl);
     virtual ~BpMemory();
     virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;
 
@@ -248,8 +253,9 @@
 }
 
 BpMemoryHeap::~BpMemoryHeap() {
-    if (mHeapId != -1) {
-        close(mHeapId);
+    int32_t heapId = mHeapId.load(memory_order_relaxed);
+    if (heapId != -1) {
+        close(heapId);
         if (mRealHeap) {
             // by construction we're the last one
             if (mBase != MAP_FAILED) {
@@ -257,7 +263,7 @@
 
                 if (VERBOSE) {
                     ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d",
-                            binder.get(), this, mSize, mHeapId);
+                            binder.get(), this, mSize, heapId);
                     CallStack stack(LOG_TAG);
                 }
 
@@ -273,17 +279,21 @@
 
 void BpMemoryHeap::assertMapped() const
 {
-    if (mHeapId == -1) {
+    int32_t heapId = mHeapId.load(memory_order_acquire);
+    if (heapId == -1) {
         sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
         sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
         heap->assertReallyMapped();
         if (heap->mBase != MAP_FAILED) {
             Mutex::Autolock _l(mLock);
-            if (mHeapId == -1) {
+            if (mHeapId.load(memory_order_relaxed) == -1) {
                 mBase   = heap->mBase;
                 mSize   = heap->mSize;
                 mOffset = heap->mOffset;
-                android_atomic_write( dup( heap->mHeapId ), &mHeapId );
+                int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0);
+                ALOGE_IF(fd==-1, "cannot dup fd=%d",
+                        heap->mHeapId.load(memory_order_relaxed));
+                mHeapId.store(fd, memory_order_release);
             }
         } else {
             // something went wrong
@@ -294,7 +304,8 @@
 
 void BpMemoryHeap::assertReallyMapped() const
 {
-    if (mHeapId == -1) {
+    int32_t heapId = mHeapId.load(memory_order_acquire);
+    if (heapId == -1) {
 
         // remote call without mLock held, worse case scenario, we end up
         // calling transact() from multiple threads, but that's not a problem,
@@ -313,8 +324,8 @@
                 parcel_fd, size, err, strerror(-err));
 
         Mutex::Autolock _l(mLock);
-        if (mHeapId == -1) {
-            int fd = dup( parcel_fd );
+        if (mHeapId.load(memory_order_relaxed) == -1) {
+            int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
             ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
                     parcel_fd, size, err, strerror(errno));
 
@@ -322,7 +333,6 @@
             if (!(flags & READ_ONLY)) {
                 access |= PROT_WRITE;
             }
-
             mRealHeap = true;
             mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
             if (mBase == MAP_FAILED) {
@@ -333,7 +343,7 @@
                 mSize = size;
                 mFlags = flags;
                 mOffset = offset;
-                android_atomic_write(fd, &mHeapId);
+                mHeapId.store(fd, memory_order_release);
             }
         }
     }
@@ -341,7 +351,8 @@
 
 int BpMemoryHeap::getHeapID() const {
     assertMapped();
-    return mHeapId;
+    // We either stored mHeapId ourselves, or loaded it with acquire semantics.
+    return mHeapId.load(memory_order_relaxed);
 }
 
 void* BpMemoryHeap::getBase() const {
@@ -418,9 +429,10 @@
                 "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                 binder.get(), info.heap.get(),
                 static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
-                static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+                static_cast<BpMemoryHeap*>(info.heap.get())
+                    ->mHeapId.load(memory_order_relaxed),
                 info.count);
-        android_atomic_inc(&info.count);
+        ++info.count;
         return info.heap;
     } else {
         heap_info_t info;
@@ -445,13 +457,13 @@
         ssize_t i = mHeapCache.indexOfKey(binder);
         if (i>=0) {
             heap_info_t& info(mHeapCache.editValueAt(i));
-            int32_t c = android_atomic_dec(&info.count);
-            if (c == 1) {
+            if (--info.count == 0) {
                 ALOGD_IF(VERBOSE,
                         "removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                         binder.unsafe_get(), info.heap.get(),
                         static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
-                        static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+                        static_cast<BpMemoryHeap*>(info.heap.get())
+                            ->mHeapId.load(memory_order_relaxed),
                         info.count);
                 rel = mHeapCache.valueAt(i).heap;
                 mHeapCache.removeItemsAt(i);
@@ -482,7 +494,7 @@
         ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
                 mHeapCache.keyAt(i).unsafe_get(),
                 info.heap.get(), info.count,
-                h->mHeapId, h->mBase, h->mSize);
+                h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize);
     }
 }
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index eccc400..e832961 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -65,10 +65,6 @@
 
 namespace android {
 
-static const char* getReturnString(size_t idx);
-static const void* printReturnCommand(TextOutput& out, const void* _cmd);
-static const void* printCommand(TextOutput& out, const void* _cmd);
-
 // Static const and functions will be optimized out if not used,
 // when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
 static const char *kReturnStrings[] = {
@@ -112,8 +108,9 @@
     "BC_DEAD_BINDER_DONE"
 };
 
-static const char* getReturnString(size_t idx)
+static const char* getReturnString(uint32_t cmd)
 {
+    size_t idx = cmd & 0xff;
     if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
         return kReturnStrings[idx];
     else
@@ -331,6 +328,7 @@
             delete st;
             pthread_setspecific(gTLS, NULL);
         }
+        pthread_key_delete(gTLS);
         gHaveTLS = false;
     }
 }
@@ -340,6 +338,11 @@
     gDisableBackgroundScheduling = disable;
 }
 
+bool IPCThreadState::backgroundSchedulingDisabled()
+{
+    return gDisableBackgroundScheduling;
+}
+
 sp<ProcessState> IPCThreadState::process()
 {
     return mProcess;
@@ -512,9 +515,9 @@
         }
     } while (result != -ECONNREFUSED && result != -EBADF);
 
-    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
-        (void*)pthread_self(), getpid(), (void*)result);
-    
+    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
+        (void*)pthread_self(), getpid(), result);
+
     mOut.writeInt32(BC_EXIT_LOOPER);
     talkWithDriver(false);
 }
@@ -655,7 +658,7 @@
     waitForResponse(NULL, &result);
 
 #if LOG_REFCOUNTS
-    printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
+    ALOGV("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
         handle, result == NO_ERROR ? "SUCCESS" : "FAILURE");
 #endif
 
@@ -670,7 +673,7 @@
 void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder)
 {
 #if LOG_REFCOUNTS
-    printf("IPCThreadState::expungeHandle(%ld)\n", handle);
+    ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle);
 #endif
     self()->mProcess->expungeHandle(handle, binder);
 }
@@ -1136,7 +1139,7 @@
         break;
 
     default:
-        printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
+        ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd);
         result = UNKNOWN_ERROR;
         break;
     }
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index 6bba996..674bddf 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -31,7 +31,7 @@
 class BpPermissionController : public BpInterface<IPermissionController>
 {
 public:
-    BpPermissionController(const sp<IBinder>& impl)
+    explicit BpPermissionController(const sp<IBinder>& impl)
         : BpInterface<IPermissionController>(impl)
     {
     }
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 76508b8..96e1a8c 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -25,7 +25,7 @@
 
 class BpProcessInfoService : public BpInterface<IProcessInfoService> {
 public:
-    BpProcessInfoService(const sp<IBinder>& impl)
+    explicit BpProcessInfoService(const sp<IBinder>& impl)
         : BpInterface<IProcessInfoService>(impl) {}
 
     virtual status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 2a22b69..646809e 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -31,7 +31,7 @@
 class BpResultReceiver : public BpInterface<IResultReceiver>
 {
 public:
-    BpResultReceiver(const sp<IBinder>& impl)
+    explicit BpResultReceiver(const sp<IBinder>& impl)
         : BpInterface<IResultReceiver>(impl)
     {
     }
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 44d235f..c7a0f43 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -23,6 +23,7 @@
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 #include <utils/SystemClock.h>
+#include <utils/CallStack.h>
 
 #include <private/binder/Static.h>
 
@@ -67,11 +68,6 @@
 
 bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
 {
-#ifdef __BRILLO__
-    // Brillo doesn't currently run ActivityManager or support framework permissions.
-    return true;
-#endif
-
     sp<IPermissionController> pc;
     gDefaultServiceManagerLock.lock();
     pc = gPermissionController;
@@ -131,7 +127,7 @@
 class BpServiceManager : public BpInterface<IServiceManager>
 {
 public:
-    BpServiceManager(const sp<IBinder>& impl)
+    explicit BpServiceManager(const sp<IBinder>& impl)
         : BpInterface<IServiceManager>(impl)
     {
     }
@@ -141,7 +137,12 @@
         unsigned n;
         for (n = 0; n < 5; n++){
             if (n > 0) {
-                ALOGI("Waiting for service %s...", String8(name).string());
+                if (!strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder")) {
+                    ALOGI("Waiting for vendor service %s...", String8(name).string());
+                    CallStack stack(LOG_TAG);
+                } else {
+                    ALOGI("Waiting for service %s...", String8(name).string());
+                }
                 sleep(1);
             }
             sp<IBinder> svc = checkService(name);
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
new file mode 100644
index 0000000..c793df3
--- /dev/null
+++ b/libs/binder/IShellCallback.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ShellCallback"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IShellCallback.h>
+
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpShellCallback : public BpInterface<IShellCallback>
+{
+public:
+    explicit BpShellCallback(const sp<IBinder>& impl)
+        : BpInterface<IShellCallback>(impl)
+    {
+    }
+
+    virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor());
+        data.writeString16(path);
+        data.writeString16(seLinuxContext);
+        remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0);
+        reply.readExceptionCode();
+        int fd = reply.readParcelFileDescriptor();
+        return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+
+    }
+};
+
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnShellCallback::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case OP_OPEN_OUTPUT_FILE: {
+            CHECK_INTERFACE(IShellCallback, data, reply);
+            String16 path(data.readString16());
+            String16 seLinuxContext(data.readString16());
+            int fd = openOutputFile(path, seLinuxContext);
+            if (reply != NULL) {
+                reply->writeNoException();
+                if (fd >= 0) {
+                    reply->writeInt32(1);
+                    reply->writeParcelFileDescriptor(fd, true);
+                } else {
+                    reply->writeInt32(0);
+                }
+            } else if (fd >= 0) {
+                close(fd);
+            }
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}; // namespace android
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
new file mode 100644
index 0000000..3a8a63c
--- /dev/null
+++ b/libs/binder/IpPrefix.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IpPrefix"
+
+#include <binder/IpPrefix.h>
+#include <vector>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::Parcel;
+using android::status_t;
+using android::UNEXPECTED_NULL;
+using namespace ::android::binder;
+
+namespace android {
+
+namespace net {
+
+#define RETURN_IF_FAILED(calledOnce)                                     \
+    {                                                                    \
+        status_t returnStatus = calledOnce;                              \
+        if (returnStatus) {                                              \
+            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+            return returnStatus;                                         \
+         }                                                               \
+    }
+
+status_t IpPrefix::writeToParcel(Parcel* parcel) const {
+    /*
+     * Keep implementation in sync with writeToParcel() in
+     * frameworks/base/core/java/android/net/IpPrefix.java.
+     */
+    std::vector<uint8_t> byte_vector;
+
+    if (mIsIpv6) {
+        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
+        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+    } else {
+        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
+        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+    }
+
+    RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
+    RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
+
+    return NO_ERROR;
+}
+
+status_t IpPrefix::readFromParcel(const Parcel* parcel) {
+    /*
+     * Keep implementation in sync with readFromParcel() in
+     * frameworks/base/core/java/android/net/IpPrefix.java.
+     */
+    std::vector<uint8_t> byte_vector;
+
+    RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
+    RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
+
+    if (byte_vector.size() == 16) {
+        mIsIpv6 = true;
+        memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
+
+    } else if (byte_vector.size() == 4) {
+        mIsIpv6 = false;
+        memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
+
+    } else {
+        ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
+{
+    return mUnion.mIn6Addr;
+}
+
+const struct in_addr& IpPrefix::getAddressAsInAddr() const
+{
+    return mUnion.mInAddr;
+}
+
+bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
+{
+    if (isIpv6()) {
+        *addr = mUnion.mIn6Addr;
+        return true;
+    }
+    return false;
+}
+
+bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
+{
+    if (isIpv4()) {
+        *addr = mUnion.mInAddr;
+        return true;
+    }
+    return false;
+}
+
+bool IpPrefix::isIpv6() const
+{
+    return mIsIpv6;
+}
+
+bool IpPrefix::isIpv4() const
+{
+    return !mIsIpv6;
+}
+
+int32_t IpPrefix::getPrefixLength() const
+{
+    return mPrefixLength;
+}
+
+void IpPrefix::setAddress(const struct in6_addr& addr)
+{
+    mUnion.mIn6Addr = addr;
+    mIsIpv6 = true;
+}
+
+void IpPrefix::setAddress(const struct in_addr& addr)
+{
+    mUnion.mInAddr = addr;
+    mIsIpv6 = false;
+}
+
+void IpPrefix::setPrefixLength(int32_t prefix)
+{
+    mPrefixLength = prefix;
+}
+
+bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
+{
+    if (lhs.mIsIpv6 != rhs.mIsIpv6) {
+        return false;
+    }
+
+    if (lhs.mPrefixLength != rhs.mPrefixLength) {
+        return false;
+    }
+
+    if (lhs.mIsIpv6) {
+        return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
+    }
+
+    return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
+}
+
+}  // namespace net
+
+}  // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 51eac11..2a15773 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -126,7 +126,7 @@
         PAGE_ALIGNED = 0x00000001
     };
 public:
-    SimpleBestFitAllocator(size_t size);
+    explicit SimpleBestFitAllocator(size_t size);
     ~SimpleBestFitAllocator();
 
     size_t      allocate(size_t size, uint32_t flags = 0);
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 43a01e4..03f00be 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -16,20 +16,19 @@
 
 #define LOG_TAG "MemoryHeapBase"
 
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
 #include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <binder/MemoryHeapBase.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#include <log/log.h>
 
 namespace android {
 
@@ -83,7 +82,7 @@
 {
     const size_t pagesize = getpagesize();
     size = ((size + pagesize-1) & ~(pagesize-1));
-    mapfd(dup(fd), size, offset);
+    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
 }
 
 status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 19ce3eb..aec8f10 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -37,6 +37,7 @@
 #include <binder/ProcessState.h>
 #include <binder/Status.h>
 #include <binder/TextOutput.h>
+#include <binder/Value.h>
 
 #include <cutils/ashmem.h>
 #include <utils/Debug.h>
@@ -100,32 +101,6 @@
     BLOB_ASHMEM_MUTABLE = 2,
 };
 
-static dev_t ashmem_rdev()
-{
-    static dev_t __ashmem_rdev;
-    static pthread_mutex_t __ashmem_rdev_lock = PTHREAD_MUTEX_INITIALIZER;
-
-    pthread_mutex_lock(&__ashmem_rdev_lock);
-
-    dev_t rdev = __ashmem_rdev;
-    if (!rdev) {
-        int fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDONLY));
-        if (fd >= 0) {
-            struct stat st;
-
-            int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
-            close(fd);
-            if ((ret >= 0) && S_ISCHR(st.st_mode)) {
-                rdev = __ashmem_rdev = st.st_rdev;
-            }
-        }
-    }
-
-    pthread_mutex_unlock(&__ashmem_rdev_lock);
-
-    return rdev;
-}
-
 void acquire_object(const sp<ProcessState>& proc,
     const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
 {
@@ -154,15 +129,11 @@
             return;
         }
         case BINDER_TYPE_FD: {
-            if ((obj.cookie != 0) && (outAshmemSize != NULL)) {
-                struct stat st;
-                int ret = fstat(obj.handle, &st);
-                if (!ret && S_ISCHR(st.st_mode) && (st.st_rdev == ashmem_rdev())) {
-                    // If we own an ashmem fd, keep track of how much memory it refers to.
-                    int size = ashmem_get_size_region(obj.handle);
-                    if (size > 0) {
-                        *outAshmemSize += size;
-                    }
+            if ((obj.cookie != 0) && (outAshmemSize != NULL) && ashmem_valid(obj.handle)) {
+                // If we own an ashmem fd, keep track of how much memory it refers to.
+                int size = ashmem_get_size_region(obj.handle);
+                if (size > 0) {
+                    *outAshmemSize += size;
                 }
             }
             return;
@@ -207,14 +178,10 @@
         }
         case BINDER_TYPE_FD: {
             if (obj.cookie != 0) { // owned
-                if (outAshmemSize != NULL) {
-                    struct stat st;
-                    int ret = fstat(obj.handle, &st);
-                    if (!ret && S_ISCHR(st.st_mode) && (st.st_rdev == ashmem_rdev())) {
-                        int size = ashmem_get_size_region(obj.handle);
-                        if (size > 0) {
-                            *outAshmemSize -= size;
-                        }
+                if ((outAshmemSize != NULL) && ashmem_valid(obj.handle)) {
+                    int size = ashmem_get_size_region(obj.handle);
+                    if (size > 0) {
+                        *outAshmemSize -= size;
                     }
                 }
 
@@ -244,7 +211,14 @@
 {
     flat_binder_object obj;
 
-    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+    if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
+        /* minimum priority for all nodes is nice 0 */
+        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
+    } else {
+        /* minimum priority for all nodes is MAX_NICE(19) */
+        obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+    }
+
     if (binder != NULL) {
         IBinder *local = binder->localBinder();
         if (!local) {
@@ -573,7 +547,7 @@
                 // If this is a file descriptor, we need to dup it so the
                 // new Parcel now owns its own fd, and can declare that we
                 // officially know we have fds.
-                flat->handle = dup(flat->handle);
+                flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
                 flat->cookie = 1;
                 mHasFds = mFdsKnown = true;
                 if (!mAllowFds) {
@@ -586,6 +560,14 @@
     return err;
 }
 
+int Parcel::compareData(const Parcel& other) {
+    size_t size = dataSize();
+    if (size != other.dataSize()) {
+        return size < other.dataSize() ? -1 : 1;
+    }
+    return memcmp(data(), other.data(), size);
+}
+
 bool Parcel::allowFds() const
 {
     return mAllowFds;
@@ -784,7 +766,7 @@
     const uint8_t* strData = (uint8_t*)str.data();
     const size_t strLen= str.length();
     const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen);
-    if (utf16Len < 0 || utf16Len> std::numeric_limits<int32_t>::max()) {
+    if (utf16Len < 0 || utf16Len > std::numeric_limits<int32_t>::max()) {
         return BAD_VALUE;
     }
 
@@ -799,7 +781,7 @@
         return NO_MEMORY;
     }
 
-    utf8_to_utf16(strData, strLen, (char16_t*)dst);
+    utf8_to_utf16(strData, strLen, (char16_t*)dst, (size_t) utf16Len + 1);
 
     return NO_ERROR;
 }
@@ -1112,7 +1094,7 @@
 }
 
 status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readStrongBinder);
+    return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
 }
 
 status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const {
@@ -1140,6 +1122,10 @@
     return parcelable.writeToParcel(this);
 }
 
+status_t Parcel::writeValue(const binder::Value& value) {
+    return value.writeToParcel(this);
+}
+
 status_t Parcel::writeNativeHandle(const native_handle* handle)
 {
     if (!handle || handle->version != sizeof(native_handle))
@@ -1176,7 +1162,7 @@
 
 status_t Parcel::writeDupFileDescriptor(int fd)
 {
-    int dupFd = dup(fd);
+    int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
     if (dupFd < 0) {
         return -errno;
     }
@@ -1187,15 +1173,21 @@
     return err;
 }
 
-status_t Parcel::writeUniqueFileDescriptor(const ScopedFd& fd) {
+status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership)
+{
+    writeInt32(0);
+    return writeFileDescriptor(fd, takeOwnership);
+}
+
+status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) {
     return writeDupFileDescriptor(fd.get());
 }
 
-status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<ScopedFd>& val) {
+status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) {
     return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
 }
 
-status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<ScopedFd>>& val) {
+status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
     return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
 }
 
@@ -1358,6 +1350,120 @@
     return status.writeToParcel(this);
 }
 
+status_t Parcel::writeMap(const ::android::binder::Map& map_in)
+{
+    using ::std::map;
+    using ::android::binder::Value;
+    using ::android::binder::Map;
+
+    Map::const_iterator iter;
+    status_t ret;
+
+    ret = writeInt32(map_in.size());
+
+    if (ret != NO_ERROR) {
+        return ret;
+    }
+
+    for (iter = map_in.begin(); iter != map_in.end(); ++iter) {
+        ret = writeValue(Value(iter->first));
+        if (ret != NO_ERROR) {
+            return ret;
+        }
+
+        ret = writeValue(iter->second);
+        if (ret != NO_ERROR) {
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map)
+{
+    if (map == NULL) {
+        return writeInt32(-1);
+    }
+
+    return writeMap(*map.get());
+}
+
+status_t Parcel::readMap(::android::binder::Map* map_out)const
+{
+    using ::std::map;
+    using ::android::String16;
+    using ::android::String8;
+    using ::android::binder::Value;
+    using ::android::binder::Map;
+
+    status_t ret = NO_ERROR;
+    int32_t count;
+
+    ret = readInt32(&count);
+    if (ret != NO_ERROR) {
+        return ret;
+    }
+
+    if (count < 0) {
+        ALOGE("readMap: Unexpected count: %d", count);
+        return (count == -1)
+            ? UNEXPECTED_NULL
+            : BAD_VALUE;
+    }
+
+    map_out->clear();
+
+    while (count--) {
+        Map::key_type key;
+        Value value;
+
+        ret = readValue(&value);
+        if (ret != NO_ERROR) {
+            return ret;
+        }
+
+        if (!value.getString(&key)) {
+            ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType());
+            return BAD_VALUE;
+        }
+
+        ret = readValue(&value);
+        if (ret != NO_ERROR) {
+            return ret;
+        }
+
+        (*map_out)[key] = value;
+    }
+
+    return ret;
+}
+
+status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const
+{
+    const size_t start = dataPosition();
+    int32_t count;
+    status_t status = readInt32(&count);
+    map->reset();
+
+    if (status != OK || count == -1) {
+        return status;
+    }
+
+    setDataPosition(start);
+    map->reset(new binder::Map());
+
+    status = readMap(map->get());
+
+    if (status != OK) {
+        map->reset();
+    }
+
+    return status;
+}
+
+
+
 void Parcel::remove(size_t /*start*/, size_t /*amt*/)
 {
     LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -1461,13 +1567,13 @@
         return status;
     }
 
-    const void* data = parcel->readInplace(size);
+    T* data = const_cast<T*>(reinterpret_cast<const T*>(parcel->readInplace(size)));
     if (!data) {
         status = BAD_VALUE;
         return status;
     }
-    val->resize(size);
-    memcpy(val->data(), data, size);
+    val->reserve(size);
+    val->insert(val->end(), data, data + size);
 
     return status;
 }
@@ -1842,13 +1948,37 @@
 
 String8 Parcel::readString8() const
 {
-    int32_t size = readInt32();
-    // watch for potential int overflow adding 1 for trailing NUL
-    if (size > 0 && size < INT32_MAX) {
-        const char* str = (const char*)readInplace(size+1);
-        if (str) return String8(str, size);
+    String8 retString;
+    status_t status = readString8(&retString);
+    if (status != OK) {
+        // We don't care about errors here, so just return an empty string.
+        return String8();
     }
-    return String8();
+    return retString;
+}
+
+status_t Parcel::readString8(String8* pArg) const
+{
+    int32_t size;
+    status_t status = readInt32(&size);
+    if (status != OK) {
+        return status;
+    }
+    // watch for potential int overflow from size+1
+    if (size < 0 || size >= INT32_MAX) {
+        return BAD_VALUE;
+    }
+    // |writeString8| writes nothing for empty string.
+    if (size == 0) {
+        *pArg = String8();
+        return OK;
+    }
+    const char* str = (const char*)readInplace(size + 1);
+    if (str == NULL) {
+        return BAD_VALUE;
+    }
+    pArg->setTo(str, size);
+    return OK;
 }
 
 String16 Parcel::readString16() const
@@ -1913,13 +2043,25 @@
 
 status_t Parcel::readStrongBinder(sp<IBinder>* val) const
 {
+    status_t status = readNullableStrongBinder(val);
+    if (status == OK && !val->get()) {
+        status = UNEXPECTED_NULL;
+    }
+    return status;
+}
+
+status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
+{
     return unflatten_binder(ProcessState::self(), *this, val);
 }
 
 sp<IBinder> Parcel::readStrongBinder() const
 {
     sp<IBinder> val;
-    readStrongBinder(&val);
+    // Note that a lot of code in Android reads binders by hand with this
+    // method, and that code has historically been ok with getting nullptr
+    // back (while ignoring error codes).
+    readNullableStrongBinder(&val);
     return val;
 }
 
@@ -1942,6 +2084,10 @@
     return parcelable->readFromParcel(this);
 }
 
+status_t Parcel::readValue(binder::Value* value) const {
+    return value->readFromParcel(this);
+}
+
 int32_t Parcel::readExceptionCode() const
 {
     binder::Status status;
@@ -1964,7 +2110,7 @@
     }
 
     for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
-        h->data[i] = dup(readFileDescriptor());
+        h->data[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
         if (h->data[i] < 0) {
             for (int j = 0; j < i; j++) {
                 close(h->data[j]);
@@ -1982,7 +2128,6 @@
     return h;
 }
 
-
 int Parcel::readFileDescriptor() const
 {
     const flat_binder_object* flat = readObject(true);
@@ -1994,7 +2139,18 @@
     return BAD_TYPE;
 }
 
-status_t Parcel::readUniqueFileDescriptor(ScopedFd* val) const
+int Parcel::readParcelFileDescriptor() const
+{
+    int32_t hasComm = readInt32();
+    int fd = readFileDescriptor();
+    if (hasComm != 0) {
+        // skip
+        readFileDescriptor();
+    }
+    return fd;
+}
+
+status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const
 {
     int got = readFileDescriptor();
 
@@ -2002,7 +2158,7 @@
         return BAD_TYPE;
     }
 
-    val->reset(dup(got));
+    val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
 
     if (val->get() < 0) {
         return BAD_VALUE;
@@ -2012,11 +2168,11 @@
 }
 
 
-status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<ScopedFd>>* val) const {
+status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
     return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
 }
 
-status_t Parcel::readUniqueFileDescriptorVector(std::vector<ScopedFd>* val) const {
+status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const {
     return readTypedVector(val, &Parcel::readUniqueFileDescriptor);
 }
 
@@ -2076,11 +2232,15 @@
 
     status_t err = NO_ERROR;
     for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
-        fds[i] = dup(this->readFileDescriptor());
-        if (fds[i] < 0) {
+        int fd = this->readFileDescriptor();
+        if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) {
             err = BAD_VALUE;
-            ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
-                i, fds[i], fd_count, strerror(errno));
+            ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
+                  i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+            // Close all the file descriptors that were dup-ed.
+            for (size_t j=0; j<i ;j++) {
+                close(fds[j]);
+            }
         }
     }
 
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index aef791c..d617b5a 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "PersistableBundle"
 
 #include <binder/PersistableBundle.h>
+#include <private/binder/ParcelValTypes.h>
 
 #include <limits>
 
@@ -32,35 +33,34 @@
 using android::sp;
 using android::status_t;
 using android::UNEXPECTED_NULL;
+using std::map;
+using std::set;
+using std::vector;
+using namespace ::android::binder;
 
 enum {
     // Keep in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java.
     BUNDLE_MAGIC = 0x4C444E42,
 };
 
-enum {
-    // Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
-    VAL_STRING = 0,
-    VAL_INTEGER = 1,
-    VAL_LONG = 6,
-    VAL_DOUBLE = 8,
-    VAL_BOOLEAN = 9,
-    VAL_STRINGARRAY = 14,
-    VAL_INTARRAY = 18,
-    VAL_LONGARRAY = 19,
-    VAL_BOOLEANARRAY = 23,
-    VAL_PERSISTABLEBUNDLE = 25,
-    VAL_DOUBLEARRAY = 28,
-};
-
 namespace {
 template <typename T>
-bool getValue(const android::String16& key, T* out, const std::map<android::String16, T>& map) {
+bool getValue(const android::String16& key, T* out, const map<android::String16, T>& map) {
     const auto& it = map.find(key);
     if (it == map.end()) return false;
     *out = it->second;
     return true;
 }
+
+template <typename T>
+set<android::String16> getKeys(const map<android::String16, T>& map) {
+    if (map.empty()) return set<android::String16>();
+    set<android::String16> keys;
+    for (const auto& key_value_pair : map) {
+        keys.emplace(key_value_pair.first);
+    }
+    return keys;
+}
 }  // namespace
 
 namespace android {
@@ -78,7 +78,7 @@
 
 #define RETURN_IF_ENTRY_ERASED(map, key)                                 \
     {                                                                    \
-        size_t num_erased = map.erase(key);                              \
+        size_t num_erased = (map).erase(key);                            \
         if (num_erased) {                                                \
             ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
             return num_erased;                                           \
@@ -188,27 +188,27 @@
     mStringMap[key] = value;
 }
 
-void PersistableBundle::putBooleanVector(const String16& key, const std::vector<bool>& value) {
+void PersistableBundle::putBooleanVector(const String16& key, const vector<bool>& value) {
     erase(key);
     mBoolVectorMap[key] = value;
 }
 
-void PersistableBundle::putIntVector(const String16& key, const std::vector<int32_t>& value) {
+void PersistableBundle::putIntVector(const String16& key, const vector<int32_t>& value) {
     erase(key);
     mIntVectorMap[key] = value;
 }
 
-void PersistableBundle::putLongVector(const String16& key, const std::vector<int64_t>& value) {
+void PersistableBundle::putLongVector(const String16& key, const vector<int64_t>& value) {
     erase(key);
     mLongVectorMap[key] = value;
 }
 
-void PersistableBundle::putDoubleVector(const String16& key, const std::vector<double>& value) {
+void PersistableBundle::putDoubleVector(const String16& key, const vector<double>& value) {
     erase(key);
     mDoubleVectorMap[key] = value;
 }
 
-void PersistableBundle::putStringVector(const String16& key, const std::vector<String16>& value) {
+void PersistableBundle::putStringVector(const String16& key, const vector<String16>& value) {
     erase(key);
     mStringVectorMap[key] = value;
 }
@@ -238,23 +238,23 @@
     return getValue(key, out, mStringMap);
 }
 
-bool PersistableBundle::getBooleanVector(const String16& key, std::vector<bool>* out) const {
+bool PersistableBundle::getBooleanVector(const String16& key, vector<bool>* out) const {
     return getValue(key, out, mBoolVectorMap);
 }
 
-bool PersistableBundle::getIntVector(const String16& key, std::vector<int32_t>* out) const {
+bool PersistableBundle::getIntVector(const String16& key, vector<int32_t>* out) const {
     return getValue(key, out, mIntVectorMap);
 }
 
-bool PersistableBundle::getLongVector(const String16& key, std::vector<int64_t>* out) const {
+bool PersistableBundle::getLongVector(const String16& key, vector<int64_t>* out) const {
     return getValue(key, out, mLongVectorMap);
 }
 
-bool PersistableBundle::getDoubleVector(const String16& key, std::vector<double>* out) const {
+bool PersistableBundle::getDoubleVector(const String16& key, vector<double>* out) const {
     return getValue(key, out, mDoubleVectorMap);
 }
 
-bool PersistableBundle::getStringVector(const String16& key, std::vector<String16>* out) const {
+bool PersistableBundle::getStringVector(const String16& key, vector<String16>* out) const {
     return getValue(key, out, mStringVectorMap);
 }
 
@@ -262,6 +262,50 @@
     return getValue(key, out, mPersistableBundleMap);
 }
 
+set<String16> PersistableBundle::getBooleanKeys() const {
+    return getKeys(mBoolMap);
+}
+
+set<String16> PersistableBundle::getIntKeys() const {
+    return getKeys(mIntMap);
+}
+
+set<String16> PersistableBundle::getLongKeys() const {
+    return getKeys(mLongMap);
+}
+
+set<String16> PersistableBundle::getDoubleKeys() const {
+    return getKeys(mDoubleMap);
+}
+
+set<String16> PersistableBundle::getStringKeys() const {
+    return getKeys(mStringMap);
+}
+
+set<String16> PersistableBundle::getBooleanVectorKeys() const {
+    return getKeys(mBoolVectorMap);
+}
+
+set<String16> PersistableBundle::getIntVectorKeys() const {
+    return getKeys(mIntVectorMap);
+}
+
+set<String16> PersistableBundle::getLongVectorKeys() const {
+    return getKeys(mLongVectorMap);
+}
+
+set<String16> PersistableBundle::getDoubleVectorKeys() const {
+    return getKeys(mDoubleVectorMap);
+}
+
+set<String16> PersistableBundle::getStringVectorKeys() const {
+    return getKeys(mStringVectorMap);
+}
+
+set<String16> PersistableBundle::getPersistableBundleKeys() const {
+    return getKeys(mPersistableBundleMap);
+}
+
 status_t PersistableBundle::writeToParcelInner(Parcel* parcel) const {
     /*
      * To keep this implementation in sync with writeArrayMapInternal() in
@@ -363,7 +407,6 @@
     RETURN_IF_FAILED(parcel->readInt32(&num_entries));
 
     for (; num_entries > 0; --num_entries) {
-        size_t start_pos = parcel->dataPosition();
         String16 key;
         int32_t value_type;
         RETURN_IF_FAILED(parcel->readString16(&key));
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index fb28643..8939d9c 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -57,6 +57,40 @@
     return TIMED_OUT;
 }
 
+status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length,
+        /*in*/ int32_t* pids, /*out*/ int32_t* states,
+        /*out*/ int32_t *scores) {
+    status_t err = NO_ERROR;
+    sp<IProcessInfoService> pis;
+    mProcessInfoLock.lock();
+    pis = mProcessInfoService;
+    mProcessInfoLock.unlock();
+
+    for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) {
+
+        if (pis != NULL) {
+            err = pis->getProcessStatesAndOomScoresFromPids(length,
+                    /*in*/ pids, /*out*/ states, /*out*/ scores);
+            if (err == NO_ERROR) return NO_ERROR; // success
+            if (IInterface::asBinder(pis)->isBinderAlive()) return err;
+        }
+        sleep(1);
+
+        mProcessInfoLock.lock();
+        if (pis == mProcessInfoService) {
+            updateBinderLocked();
+        }
+        pis = mProcessInfoService;
+        mProcessInfoLock.unlock();
+    }
+
+    ALOGW("%s: Could not retrieve process states and scores "
+            "from ProcessInfoService after %d retries.", __FUNCTION__,
+            BINDER_ATTEMPT_LIMIT);
+
+    return TIMED_OUT;
+}
+
 void ProcessInfoService::updateBinderLocked() {
     const sp<IServiceManager> sm(defaultServiceManager());
     if (sm != NULL) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f13f49f..add5e74 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -16,8 +16,6 @@
 
 #define LOG_TAG "ProcessState"
 
-#include <cutils/process_name.h>
-
 #include <binder/ProcessState.h>
 
 #include <utils/Atomic.h>
@@ -42,7 +40,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
+#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
 #define DEFAULT_MAX_BINDER_THREADS 15
 
 // -------------------------------------------------------------------------
@@ -52,7 +50,7 @@
 class PoolThread : public Thread
 {
 public:
-    PoolThread(bool isMain)
+    explicit PoolThread(bool isMain)
         : mIsMain(isMain)
     {
     }
@@ -73,7 +71,22 @@
     if (gProcess != NULL) {
         return gProcess;
     }
-    gProcess = new ProcessState;
+    gProcess = new ProcessState("/dev/binder");
+    return gProcess;
+}
+
+sp<ProcessState> ProcessState::initWithDriver(const char* driver)
+{
+    Mutex::Autolock _l(gProcessMutex);
+    if (gProcess != NULL) {
+        // Allow for initWithDriver to be called repeatedly with the same
+        // driver.
+        if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
+            return gProcess;
+        }
+        LOG_ALWAYS_FATAL("ProcessState was already initialized.");
+    }
+    gProcess = new ProcessState(driver);
     return gProcess;
 }
 
@@ -309,9 +322,13 @@
     androidSetThreadName( makeBinderThreadName().string() );
 }
 
-static int open_driver()
+String8 ProcessState::getDriverName() {
+    return mDriverName;
+}
+
+static int open_driver(const char *driver)
 {
-    int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+    int fd = open(driver, O_RDWR | O_CLOEXEC);
     if (fd >= 0) {
         int vers = 0;
         status_t result = ioctl(fd, BINDER_VERSION, &vers);
@@ -321,7 +338,8 @@
             fd = -1;
         }
         if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
-            ALOGE("Binder driver protocol does not match user space protocol!");
+          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
+                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
             close(fd);
             fd = -1;
         }
@@ -331,13 +349,14 @@
             ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
         }
     } else {
-        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
     }
     return fd;
 }
 
-ProcessState::ProcessState()
-    : mDriverFD(open_driver())
+ProcessState::ProcessState(const char *driver)
+    : mDriverName(String8(driver))
+    , mDriverFD(open_driver(driver))
     , mVMStart(MAP_FAILED)
     , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
     , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
@@ -358,6 +377,7 @@
             ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
             close(mDriverFD);
             mDriverFD = -1;
+            mDriverName.clear();
         }
     }
 
@@ -366,6 +386,13 @@
 
 ProcessState::~ProcessState()
 {
+    if (mDriverFD >= 0) {
+        if (mVMStart != MAP_FAILED) {
+            munmap(mVMStart, BINDER_VM_SIZE);
+        }
+        close(mDriverFD);
+    }
+    mDriverFD = -1;
 }
         
 }; // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index cd9509f..f0613d1 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -48,7 +48,7 @@
 class FdTextOutput : public BufferedTextOutput
 {
 public:
-    FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
+    explicit FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
     virtual ~FdTextOutput() { };
 
 protected:
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index d3520d6..006f7f9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -32,6 +32,11 @@
     return Status(exceptionCode, OK, message);
 }
 
+Status Status::fromExceptionCode(int32_t exceptionCode,
+                                 const char* message) {
+    return fromExceptionCode(exceptionCode, String8(message));
+}
+
 Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode) {
     return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode);
 }
@@ -41,6 +46,11 @@
     return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode, message);
 }
 
+Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+                                        const char* message) {
+    return fromServiceSpecificError(serviceSpecificErrorCode, String8(message));
+}
+
 Status Status::fromStatusT(status_t status) {
     Status ret;
     ret.setFromStatusT(status);
@@ -94,6 +104,16 @@
 
     if (mException == EX_SERVICE_SPECIFIC) {
         status = parcel.readInt32(&mErrorCode);
+    } else if (mException == EX_PARCELABLE) {
+        // Skip over the blob of Parcelable data
+        const int32_t header_start = parcel.dataPosition();
+        int32_t header_size;
+        status = parcel.readInt32(&header_size);
+        if (status != OK) {
+            setFromStatusT(status);
+            return status;
+        }
+        parcel.setDataPosition(header_start + header_size);
     }
     if (status != OK) {
         setFromStatusT(status);
@@ -117,11 +137,12 @@
         return status;
     }
     status = parcel->writeString16(String16(mMessage));
-    if (mException != EX_SERVICE_SPECIFIC) {
-        // We have no more information to write.
-        return status;
+    if (mException == EX_SERVICE_SPECIFIC) {
+        status = parcel->writeInt32(mErrorCode);
+    } else if (mException == EX_PARCELABLE) {
+        // Sending Parcelable blobs currently not supported
+        status = parcel->writeInt32(0);
     }
-    status = parcel->writeInt32(mErrorCode);
     return status;
 }
 
@@ -158,5 +179,10 @@
     return ret;
 }
 
+std::stringstream& operator<< (std::stringstream& stream, const Status& s) {
+    stream << s.toString8().string();
+    return stream;
+}
+
 }  // namespace binder
 }  // namespace android
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 2ed5188..101eba3 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -29,111 +29,14 @@
 
 // ---------------------------------------------------------------------------
 
-TextOutput::TextOutput() { 
+TextOutput::TextOutput() {
 }
 
-TextOutput::~TextOutput() { 
+TextOutput::~TextOutput() {
 }
 
 // ---------------------------------------------------------------------------
 
-TextOutput& operator<<(TextOutput& to, bool val)
-{
-    if (val) to.print("true", 4);
-    else to.print("false", 5);
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, int val)
-{
-    char buf[16];
-    sprintf(buf, "%d", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long val)
-{
-    char buf[16];
-    sprintf(buf, "%ld", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned int val)
-{
-    char buf[16];
-    sprintf(buf, "%u", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long val)
-{
-    char buf[16];
-    sprintf(buf, "%lu", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long long val)
-{
-    char buf[32];
-    sprintf(buf, "%Ld", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long long val)
-{
-    char buf[32];
-    sprintf(buf, "%Lu", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-static TextOutput& print_float(TextOutput& to, double value)
-{
-    char buf[64];
-    sprintf(buf, "%g", value);
-    if( !strchr(buf, '.') && !strchr(buf, 'e') &&
-        !strchr(buf, 'E') ) {
-        strncat(buf, ".0", sizeof(buf)-1);
-    }
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, float val)
-{
-    return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, double val)
-{
-    return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, const void* val)
-{
-    char buf[32];
-    snprintf(buf, sizeof(buf), "%p", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
-    to << val.string();
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String16& val)
-{
-    to << String8(val).string();
-    return to;
-}
-
 static void textOutputPrinter(void* cookie, const char* txt)
 {
     ((TextOutput*)cookie)->print(txt, strlen(txt));
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
new file mode 100644
index 0000000..fd1dfd5
--- /dev/null
+++ b/libs/binder/Value.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Value"
+
+#include <binder/Value.h>
+
+#include <limits>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/Map.h>
+#include <private/binder/ParcelValTypes.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::UNEXPECTED_NULL;
+using android::Parcel;
+using android::sp;
+using android::status_t;
+using std::map;
+using std::set;
+using std::vector;
+using android::binder::Value;
+using android::IBinder;
+using android::os::PersistableBundle;
+using namespace android::binder;
+
+// ====================================================================
+
+#define RETURN_IF_FAILED(calledOnce)                                     \
+    do {                                                                 \
+        status_t returnStatus = calledOnce;                              \
+        if (returnStatus) {                                              \
+            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+            return returnStatus;                                         \
+         }                                                               \
+    } while(false)
+
+// ====================================================================
+
+/* These `internal_type_ptr()` functions allow this
+ * class to work without C++ RTTI support. This technique
+ * only works properly when called directly from this file,
+ * but that is OK because that is the only place we will
+ * be calling them from. */
+template<class T> const void* internal_type_ptr()
+{
+    static const T *marker;
+    return (void*)&marker;
+}
+
+/* Allows the type to be specified by the argument
+ * instead of inside angle brackets. */
+template<class T> const void* internal_type_ptr(const T&)
+{
+    return internal_type_ptr<T>();
+}
+
+// ====================================================================
+
+namespace android {
+
+namespace binder {
+
+class Value::ContentBase {
+public:
+    virtual ~ContentBase() = default;
+    virtual const void* type_ptr() const = 0;
+    virtual ContentBase * clone() const = 0;
+    virtual bool operator==(const ContentBase& rhs) const = 0;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+    virtual const std::type_info &type() const = 0;
+#endif
+
+    template<typename T> bool get(T* out) const;
+};
+
+/* This is the actual class that holds the value. */
+template<typename T> class Value::Content : public Value::ContentBase {
+public:
+    Content() = default;
+    Content(const T & value) : mValue(value) { }
+
+    virtual ~Content() = default;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+    virtual const std::type_info &type() const override
+    {
+        return typeid(T);
+    }
+#endif
+
+    virtual const void* type_ptr() const override
+    {
+        return internal_type_ptr<T>();
+    }
+
+    virtual ContentBase * clone() const override
+    {
+        return new Content(mValue);
+    };
+
+    virtual bool operator==(const ContentBase& rhs) const override
+    {
+        if (type_ptr() != rhs.type_ptr()) {
+            return false;
+        }
+        return mValue == static_cast<const Content<T>* >(&rhs)->mValue;
+    }
+
+    T mValue;
+};
+
+template<typename T> bool Value::ContentBase::get(T* out) const
+{
+    if (internal_type_ptr(*out) != type_ptr())
+    {
+        return false;
+    }
+
+    *out = static_cast<const Content<T>*>(this)->mValue;
+
+    return true;
+}
+
+// ====================================================================
+
+Value::Value() : mContent(NULL)
+{
+}
+
+Value::Value(const Value& value)
+    : mContent(value.mContent ? value.mContent->clone() : NULL)
+{
+}
+
+Value::~Value()
+{
+    delete mContent;
+}
+
+bool Value::operator==(const Value& rhs) const
+{
+    const Value& lhs(*this);
+
+    if (lhs.empty() && rhs.empty()) {
+        return true;
+    }
+
+    if ( (lhs.mContent == NULL)
+      || (rhs.mContent == NULL)
+    ) {
+        return false;
+    }
+
+    return *lhs.mContent == *rhs.mContent;
+}
+
+Value& Value::swap(Value &rhs)
+{
+    std::swap(mContent, rhs.mContent);
+    return *this;
+}
+
+Value& Value::operator=(const Value& rhs)
+{
+    delete mContent;
+    mContent = rhs.mContent
+        ? rhs.mContent->clone()
+        : NULL;
+    return *this;
+}
+
+bool Value::empty() const
+{
+    return mContent == NULL;
+}
+
+void Value::clear()
+{
+    delete mContent;
+    mContent = NULL;
+}
+
+int32_t Value::parcelType() const
+{
+    const void* t_info(mContent ? mContent->type_ptr() : NULL);
+
+    if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN;
+    if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE;
+    if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER;
+    if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG;
+    if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE;
+    if (t_info == internal_type_ptr<String16>()) return VAL_STRING;
+
+    if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY;
+    if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY;
+    if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY;
+    if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY;
+    if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY;
+    if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY;
+
+    if (t_info == internal_type_ptr<Map>()) return VAL_MAP;
+    if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE;
+
+    return VAL_NULL;
+}
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+const std::type_info& Value::type() const
+{
+    return mContent != NULL
+        ? mContent->type()
+        : typeid(void);
+}
+#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+
+#define DEF_TYPE_ACCESSORS(T, TYPENAME)                      \
+    bool Value::is ## TYPENAME() const                       \
+    {                                                        \
+        return mContent                                      \
+            ? internal_type_ptr<T>() == mContent->type_ptr() \
+            : false;                                         \
+    }                                                        \
+    bool Value::get ## TYPENAME(T* out) const                \
+    {                                                        \
+        return mContent                                      \
+            ? mContent->get(out)                             \
+            : false;                                         \
+    }                                                        \
+    void Value::put ## TYPENAME(const T& in)                 \
+    {                                                        \
+        *this = in;                                          \
+    }                                                        \
+    Value& Value::operator=(const T& rhs)                    \
+    {                                                        \
+        delete mContent;                                     \
+        mContent = new Content< T >(rhs);                    \
+        return *this;                                        \
+    }                                                        \
+    Value::Value(const T& value)                             \
+        : mContent(new Content< T >(value))                  \
+    { }
+
+DEF_TYPE_ACCESSORS(bool, Boolean)
+DEF_TYPE_ACCESSORS(int8_t, Byte)
+DEF_TYPE_ACCESSORS(int32_t, Int)
+DEF_TYPE_ACCESSORS(int64_t, Long)
+DEF_TYPE_ACCESSORS(double, Double)
+DEF_TYPE_ACCESSORS(String16, String)
+
+DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector)
+DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector)
+DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector)
+DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector)
+DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector)
+DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector)
+
+DEF_TYPE_ACCESSORS(::android::binder::Map, Map)
+DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle)
+
+bool Value::getString(String8* out) const
+{
+    String16 val;
+    bool ret = getString(&val);
+    if (ret) {
+        *out = String8(val);
+    }
+    return ret;
+}
+
+bool Value::getString(::std::string* out) const
+{
+    String8 val;
+    bool ret = getString(&val);
+    if (ret) {
+        *out = val.string();
+    }
+    return ret;
+}
+
+status_t Value::writeToParcel(Parcel* parcel) const
+{
+    // This implementation needs to be kept in sync with the writeValue
+    // implementation in frameworks/base/core/java/android/os/Parcel.java
+
+#define BEGIN_HANDLE_WRITE()                                                                      \
+    do {                                                                                          \
+        const void* t_info(mContent?mContent->type_ptr():NULL);                                   \
+        if (false) { }
+#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD)                                                 \
+    else if (t_info == internal_type_ptr<T>()) {                                                  \
+        RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL));                                            \
+        RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue));   \
+    }
+#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL)                                                       \
+    else if (t_info == internal_type_ptr<T>()) {                                                  \
+        RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL));                                            \
+        RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \
+    }
+#define END_HANDLE_WRITE()                                                                        \
+        else {                                                                                    \
+            ALOGE("writeToParcel: Type not supported");                                           \
+            return BAD_TYPE;                                                                      \
+        }                                                                                         \
+    } while (false);
+
+    BEGIN_HANDLE_WRITE()
+
+    HANDLE_WRITE_TYPE(bool,     VAL_BOOLEAN, writeBool)
+    HANDLE_WRITE_TYPE(int8_t,   VAL_BYTE,    writeByte)
+    HANDLE_WRITE_TYPE(int8_t,   VAL_BYTE,    writeByte)
+    HANDLE_WRITE_TYPE(int32_t,  VAL_INTEGER, writeInt32)
+    HANDLE_WRITE_TYPE(int64_t,  VAL_LONG,    writeInt64)
+    HANDLE_WRITE_TYPE(double,   VAL_DOUBLE,  writeDouble)
+    HANDLE_WRITE_TYPE(String16, VAL_STRING,  writeString16)
+
+    HANDLE_WRITE_TYPE(vector<bool>,     VAL_BOOLEANARRAY, writeBoolVector)
+    HANDLE_WRITE_TYPE(vector<uint8_t>,  VAL_BYTEARRAY,    writeByteVector)
+    HANDLE_WRITE_TYPE(vector<int8_t>,   VAL_BYTEARRAY,    writeByteVector)
+    HANDLE_WRITE_TYPE(vector<int32_t>,  VAL_INTARRAY,     writeInt32Vector)
+    HANDLE_WRITE_TYPE(vector<int64_t>,  VAL_LONGARRAY,    writeInt64Vector)
+    HANDLE_WRITE_TYPE(vector<double>,   VAL_DOUBLEARRAY,  writeDoubleVector)
+    HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY,  writeString16Vector)
+
+    HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+    END_HANDLE_WRITE()
+
+    return NO_ERROR;
+
+#undef BEGIN_HANDLE_WRITE
+#undef HANDLE_WRITE_TYPE
+#undef HANDLE_WRITE_PARCELABLE
+#undef END_HANDLE_WRITE
+}
+
+status_t Value::readFromParcel(const Parcel* parcel)
+{
+    // This implementation needs to be kept in sync with the readValue
+    // implementation in frameworks/base/core/java/android/os/Parcel.javai
+
+#define BEGIN_HANDLE_READ()                                                                      \
+    switch(value_type) {                                                                         \
+        default:                                                                                 \
+            ALOGE("readFromParcel: Parcel type %d is not supported", value_type);                \
+            return BAD_TYPE;
+#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD)                                                 \
+        case TYPEVAL:                                                                            \
+            mContent = new Content<T>();                                                         \
+            RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue));   \
+            break;
+#define HANDLE_READ_PARCELABLE(T, TYPEVAL)                                                       \
+        case TYPEVAL:                                                                            \
+            mContent = new Content<T>();                                                         \
+            RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \
+            break;
+#define END_HANDLE_READ()                                                                        \
+    }
+
+    int32_t value_type = VAL_NULL;
+
+    delete mContent;
+    mContent = NULL;
+
+    RETURN_IF_FAILED(parcel->readInt32(&value_type));
+
+    BEGIN_HANDLE_READ()
+
+    HANDLE_READ_TYPE(bool,     VAL_BOOLEAN, readBool)
+    HANDLE_READ_TYPE(int8_t,   VAL_BYTE,    readByte)
+    HANDLE_READ_TYPE(int32_t,  VAL_INTEGER, readInt32)
+    HANDLE_READ_TYPE(int64_t,  VAL_LONG,    readInt64)
+    HANDLE_READ_TYPE(double,   VAL_DOUBLE,  readDouble)
+    HANDLE_READ_TYPE(String16, VAL_STRING,  readString16)
+
+    HANDLE_READ_TYPE(vector<bool>,     VAL_BOOLEANARRAY, readBoolVector)
+    HANDLE_READ_TYPE(vector<uint8_t>,  VAL_BYTEARRAY,    readByteVector)
+    HANDLE_READ_TYPE(vector<int32_t>,  VAL_INTARRAY,     readInt32Vector)
+    HANDLE_READ_TYPE(vector<int64_t>,  VAL_LONGARRAY,    readInt64Vector)
+    HANDLE_READ_TYPE(vector<double>,   VAL_DOUBLEARRAY,  readDoubleVector)
+    HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY,  readString16Vector)
+
+    HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+    END_HANDLE_READ()
+
+    return NO_ERROR;
+
+#undef BEGIN_HANDLE_READ
+#undef HANDLE_READ_TYPE
+#undef HANDLE_READ_PARCELABLE
+#undef END_HANDLE_READ
+}
+
+}  // namespace binder
+
+}  // namespace android
+
+/* vim: set ts=4 sw=4 tw=0 et :*/
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
new file mode 100644
index 0000000..4212776
--- /dev/null
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_APP_OPS_MANAGER_H
+#define ANDROID_APP_OPS_MANAGER_H
+
+#include <binder/IAppOpsService.h>
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class AppOpsManager
+{
+public:
+    enum {
+        MODE_ALLOWED = IAppOpsService::MODE_ALLOWED,
+        MODE_IGNORED = IAppOpsService::MODE_IGNORED,
+        MODE_ERRORED = IAppOpsService::MODE_ERRORED
+    };
+
+    enum {
+        OP_NONE = -1,
+        OP_COARSE_LOCATION = 0,
+        OP_FINE_LOCATION = 1,
+        OP_GPS = 2,
+        OP_VIBRATE = 3,
+        OP_READ_CONTACTS = 4,
+        OP_WRITE_CONTACTS = 5,
+        OP_READ_CALL_LOG = 6,
+        OP_WRITE_CALL_LOG = 7,
+        OP_READ_CALENDAR = 8,
+        OP_WRITE_CALENDAR = 9,
+        OP_WIFI_SCAN = 10,
+        OP_POST_NOTIFICATION = 11,
+        OP_NEIGHBORING_CELLS = 12,
+        OP_CALL_PHONE = 13,
+        OP_READ_SMS = 14,
+        OP_WRITE_SMS = 15,
+        OP_RECEIVE_SMS = 16,
+        OP_RECEIVE_EMERGECY_SMS = 17,
+        OP_RECEIVE_MMS = 18,
+        OP_RECEIVE_WAP_PUSH = 19,
+        OP_SEND_SMS = 20,
+        OP_READ_ICC_SMS = 21,
+        OP_WRITE_ICC_SMS = 22,
+        OP_WRITE_SETTINGS = 23,
+        OP_SYSTEM_ALERT_WINDOW = 24,
+        OP_ACCESS_NOTIFICATIONS = 25,
+        OP_CAMERA = 26,
+        OP_RECORD_AUDIO = 27,
+        OP_PLAY_AUDIO = 28,
+        OP_READ_CLIPBOARD = 29,
+        OP_WRITE_CLIPBOARD = 30,
+        OP_TAKE_MEDIA_BUTTONS = 31,
+        OP_TAKE_AUDIO_FOCUS = 32,
+        OP_AUDIO_MASTER_VOLUME = 33,
+        OP_AUDIO_VOICE_VOLUME = 34,
+        OP_AUDIO_RING_VOLUME = 35,
+        OP_AUDIO_MEDIA_VOLUME = 36,
+        OP_AUDIO_ALARM_VOLUME = 37,
+        OP_AUDIO_NOTIFICATION_VOLUME = 38,
+        OP_AUDIO_BLUETOOTH_VOLUME = 39,
+        OP_WAKE_LOCK = 40,
+        OP_MONITOR_LOCATION = 41,
+        OP_MONITOR_HIGH_POWER_LOCATION = 42,
+        OP_GET_USAGE_STATS = 43,
+        OP_MUTE_MICROPHONE = 44,
+        OP_TOAST_WINDOW = 45,
+        OP_PROJECT_MEDIA = 46,
+        OP_ACTIVATE_VPN = 47,
+        OP_WRITE_WALLPAPER = 48,
+        OP_ASSIST_STRUCTURE = 49,
+        OP_ASSIST_SCREENSHOT = 50,
+        OP_READ_PHONE_STATE = 51,
+        OP_ADD_VOICEMAIL = 52,
+        OP_USE_SIP = 53,
+        OP_PROCESS_OUTGOING_CALLS = 54,
+        OP_USE_FINGERPRINT = 55,
+        OP_BODY_SENSORS = 56,
+        OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+    };
+
+    AppOpsManager();
+
+    int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
+    int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
+    int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage);
+    void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+    void startWatchingMode(int32_t op, const String16& packageName,
+            const sp<IAppOpsCallback>& callback);
+    void stopWatchingMode(const sp<IAppOpsCallback>& callback);
+    int32_t permissionToOpCode(const String16& permission);
+
+private:
+    Mutex mLock;
+    sp<IAppOpsService> mService;
+
+    sp<IAppOpsService> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
new file mode 100644
index 0000000..3404881
--- /dev/null
+++ b/libs/binder/include/binder/Binder.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BINDER_H
+#define ANDROID_BINDER_H
+
+#include <atomic>
+#include <stdint.h>
+#include <binder/IBinder.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder : public IBinder
+{
+public:
+                        BBinder();
+
+    virtual const String16& getInterfaceDescriptor() const;
+    virtual bool        isBinderAlive() const;
+    virtual status_t    pingBinder();
+    virtual status_t    dump(int fd, const Vector<String16>& args);
+
+    virtual status_t    transact(   uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+
+    virtual status_t    linkToDeath(const sp<DeathRecipient>& recipient,
+                                    void* cookie = NULL,
+                                    uint32_t flags = 0);
+
+    virtual status_t    unlinkToDeath(  const wp<DeathRecipient>& recipient,
+                                        void* cookie = NULL,
+                                        uint32_t flags = 0,
+                                        wp<DeathRecipient>* outRecipient = NULL);
+
+    virtual void        attachObject(   const void* objectID,
+                                        void* object,
+                                        void* cleanupCookie,
+                                        object_cleanup_func func);
+    virtual void*       findObject(const void* objectID) const;
+    virtual void        detachObject(const void* objectID);
+
+    virtual BBinder*    localBinder();
+
+protected:
+    virtual             ~BBinder();
+
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+
+private:
+                        BBinder(const BBinder& o);
+            BBinder&    operator=(const BBinder& o);
+
+    class Extras;
+
+    std::atomic<Extras*> mExtras;
+            void*       mReserved0;
+};
+
+// ---------------------------------------------------------------------------
+
+class BpRefBase : public virtual RefBase
+{
+protected:
+    explicit                BpRefBase(const sp<IBinder>& o);
+    virtual                 ~BpRefBase();
+    virtual void            onFirstRef();
+    virtual void            onLastStrongRef(const void* id);
+    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
+
+    inline  IBinder*        remote()                { return mRemote; }
+    inline  IBinder*        remote() const          { return mRemote; }
+
+private:
+                            BpRefBase(const BpRefBase& o);
+    BpRefBase&              operator=(const BpRefBase& o);
+
+    IBinder* const          mRemote;
+    RefBase::weakref_type*  mRefs;
+    std::atomic<int32_t>    mState;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_BINDER_H
diff --git a/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
similarity index 100%
rename from include/binder/BinderService.h
rename to libs/binder/include/binder/BinderService.h
diff --git a/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
similarity index 100%
rename from include/binder/BpBinder.h
rename to libs/binder/include/binder/BpBinder.h
diff --git a/include/binder/BufferedTextOutput.h b/libs/binder/include/binder/BufferedTextOutput.h
similarity index 100%
rename from include/binder/BufferedTextOutput.h
rename to libs/binder/include/binder/BufferedTextOutput.h
diff --git a/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
similarity index 100%
rename from include/binder/Debug.h
rename to libs/binder/include/binder/Debug.h
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
new file mode 100644
index 0000000..5ad2180
--- /dev/null
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IACTIVITY_MANAGER_H
+#define ANDROID_IACTIVITY_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class IActivityManager : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ActivityManager)
+
+    virtual int openContentUri(const String16& /* stringUri */) = 0;
+
+    enum {
+        OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+    };
+};
+
+// ------------------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IACTIVITY_MANAGER_H
\ No newline at end of file
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
new file mode 100644
index 0000000..b62e9e2
--- /dev/null
+++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_IAPP_OPS_CALLBACK_H
+#define ANDROID_IAPP_OPS_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsCallback : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(AppOpsCallback)
+
+    virtual void opChanged(int32_t op, const String16& packageName) = 0;
+
+    enum {
+        OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_CALLBACK_H
+
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
new file mode 100644
index 0000000..dc18045
--- /dev/null
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_IAPP_OPS_SERVICE_H
+#define ANDROID_IAPP_OPS_SERVICE_H
+
+#include <binder/IAppOpsCallback.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsService : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(AppOpsService)
+
+    virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+    virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+            const String16& packageName) = 0;
+    virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+            const String16& packageName) = 0;
+    virtual void startWatchingMode(int32_t op, const String16& packageName,
+            const sp<IAppOpsCallback>& callback) = 0;
+    virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
+    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
+    virtual int32_t permissionToOpCode(const String16& permission) = 0;
+
+    enum {
+        CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
+        START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
+        FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
+        START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
+        STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
+        GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+    };
+
+    enum {
+        MODE_ALLOWED = 0,
+        MODE_IGNORED = 1,
+        MODE_ERRORED = 2
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsService : public BnInterface<IAppOpsService>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
new file mode 100644
index 0000000..e15d6f0
--- /dev/null
+++ b/libs/binder/include/binder/IBatteryStats.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IBATTERYSTATS_H
+#define ANDROID_IBATTERYSTATS_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IBatteryStats : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(BatteryStats)
+
+    virtual void noteStartSensor(int uid, int sensor) = 0;
+    virtual void noteStopSensor(int uid, int sensor) = 0;
+    virtual void noteStartVideo(int uid) = 0;
+    virtual void noteStopVideo(int uid) = 0;
+    virtual void noteStartAudio(int uid) = 0;
+    virtual void noteStopAudio(int uid) = 0;
+    virtual void noteResetVideo() = 0;
+    virtual void noteResetAudio() = 0;
+    virtual void noteFlashlightOn(int uid) = 0;
+    virtual void noteFlashlightOff(int uid) = 0;
+    virtual void noteStartCamera(int uid) = 0;
+    virtual void noteStopCamera(int uid) = 0;
+    virtual void noteResetCamera() = 0;
+    virtual void noteResetFlashlight() = 0;
+
+    enum {
+        NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        NOTE_STOP_SENSOR_TRANSACTION,
+        NOTE_START_VIDEO_TRANSACTION,
+        NOTE_STOP_VIDEO_TRANSACTION,
+        NOTE_START_AUDIO_TRANSACTION,
+        NOTE_STOP_AUDIO_TRANSACTION,
+        NOTE_RESET_VIDEO_TRANSACTION,
+        NOTE_RESET_AUDIO_TRANSACTION,
+        NOTE_FLASHLIGHT_ON_TRANSACTION,
+        NOTE_FLASHLIGHT_OFF_TRANSACTION,
+        NOTE_START_CAMERA_TRANSACTION,
+        NOTE_STOP_CAMERA_TRANSACTION,
+        NOTE_RESET_CAMERA_TRANSACTION,
+        NOTE_RESET_FLASHLIGHT_TRANSACTION
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnBatteryStats : public BnInterface<IBatteryStats>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IBATTERYSTATS_H
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
new file mode 100644
index 0000000..2e62957
--- /dev/null
+++ b/libs/binder/include/binder/IBinder.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IBINDER_H
+#define ANDROID_IBINDER_H
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+
+// linux/binder.h already defines this, but we can't just include it from there
+// because there are host builds that include this file.
+#ifndef B_PACK_CHARS
+#define B_PACK_CHARS(c1, c2, c3, c4) \
+    ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
+#endif  // B_PACK_CHARS
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder;
+class BpBinder;
+class IInterface;
+class Parcel;
+class IResultReceiver;
+class IShellCallback;
+
+/**
+ * Base class and low-level protocol for a remotable object.
+ * You can derive from this class to create an object for which other
+ * processes can hold references to it.  Communication between processes
+ * (method calls, property get and set) is down through a low-level
+ * protocol implemented on top of the transact() API.
+ */
+class IBinder : public virtual RefBase
+{
+public:
+    enum {
+        FIRST_CALL_TRANSACTION  = 0x00000001,
+        LAST_CALL_TRANSACTION   = 0x00ffffff,
+
+        PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
+        DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
+        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
+        INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
+        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),
+
+        // Corresponds to TF_ONE_WAY -- an asynchronous call.
+        FLAG_ONEWAY             = 0x00000001
+    };
+
+                          IBinder();
+
+    /**
+     * Check if this IBinder implements the interface named by
+     * @a descriptor.  If it does, the base pointer to it is returned,
+     * which you can safely static_cast<> to the concrete C++ interface.
+     */
+    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
+
+    /**
+     * Return the canonical name of the interface provided by this IBinder
+     * object.
+     */
+    virtual const String16& getInterfaceDescriptor() const = 0;
+
+    virtual bool            isBinderAlive() const = 0;
+    virtual status_t        pingBinder() = 0;
+    virtual status_t        dump(int fd, const Vector<String16>& args) = 0;
+    static  status_t        shellCommand(const sp<IBinder>& target, int in, int out, int err,
+                                         Vector<String16>& args, const sp<IShellCallback>& callback,
+                                         const sp<IResultReceiver>& resultReceiver);
+
+    virtual status_t        transact(   uint32_t code,
+                                        const Parcel& data,
+                                        Parcel* reply,
+                                        uint32_t flags = 0) = 0;
+
+    // DeathRecipient is pure abstract, there is no virtual method
+    // implementation to put in a translation unit in order to silence the
+    // weak vtables warning.
+    #if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wweak-vtables"
+    #endif
+
+    class DeathRecipient : public virtual RefBase
+    {
+    public:
+        virtual void binderDied(const wp<IBinder>& who) = 0;
+    };
+
+    #if defined(__clang__)
+    #pragma clang diagnostic pop
+    #endif
+
+    /**
+     * Register the @a recipient for a notification if this binder
+     * goes away.  If this binder object unexpectedly goes away
+     * (typically because its hosting process has been killed),
+     * then DeathRecipient::binderDied() will be called with a reference
+     * to this.
+     *
+     * The @a cookie is optional -- if non-NULL, it should be a
+     * memory address that you own (that is, you know it is unique).
+     *
+     * @note You will only receive death notifications for remote binders,
+     * as local binders by definition can't die without you dying as well.
+     * Trying to use this function on a local binder will result in an
+     * INVALID_OPERATION code being returned and nothing happening.
+     *
+     * @note This link always holds a weak reference to its recipient.
+     *
+     * @note You will only receive a weak reference to the dead
+     * binder.  You should not try to promote this to a strong reference.
+     * (Nor should you need to, as there is nothing useful you can
+     * directly do with it now that it has passed on.)
+     */
+    virtual status_t        linkToDeath(const sp<DeathRecipient>& recipient,
+                                        void* cookie = NULL,
+                                        uint32_t flags = 0) = 0;
+
+    /**
+     * Remove a previously registered death notification.
+     * The @a recipient will no longer be called if this object
+     * dies.  The @a cookie is optional.  If non-NULL, you can
+     * supply a NULL @a recipient, and the recipient previously
+     * added with that cookie will be unlinked.
+     */
+    virtual status_t        unlinkToDeath(  const wp<DeathRecipient>& recipient,
+                                            void* cookie = NULL,
+                                            uint32_t flags = 0,
+                                            wp<DeathRecipient>* outRecipient = NULL) = 0;
+
+    virtual bool            checkSubclass(const void* subclassID) const;
+
+    typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
+
+    virtual void            attachObject(   const void* objectID,
+                                            void* object,
+                                            void* cleanupCookie,
+                                            object_cleanup_func func) = 0;
+    virtual void*           findObject(const void* objectID) const = 0;
+    virtual void            detachObject(const void* objectID) = 0;
+
+    virtual BBinder*        localBinder();
+    virtual BpBinder*       remoteBinder();
+
+protected:
+    virtual          ~IBinder();
+
+private:
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IBINDER_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
new file mode 100644
index 0000000..0f1fe5b
--- /dev/null
+++ b/libs/binder/include/binder/IInterface.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_IINTERFACE_H
+#define ANDROID_IINTERFACE_H
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IInterface : public virtual RefBase
+{
+public:
+            IInterface();
+            static sp<IBinder>  asBinder(const IInterface*);
+            static sp<IBinder>  asBinder(const sp<IInterface>&);
+
+protected:
+    virtual                     ~IInterface();
+    virtual IBinder*            onAsBinder() = 0;
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
+{
+    return INTERFACE::asInterface(obj);
+}
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BnInterface : public INTERFACE, public BBinder
+{
+public:
+    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
+    virtual const String16&     getInterfaceDescriptor() const;
+
+protected:
+    virtual IBinder*            onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BpInterface : public INTERFACE, public BpRefBase
+{
+public:
+    explicit                    BpInterface(const sp<IBinder>& remote);
+
+protected:
+    virtual IBinder*            onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+#define DECLARE_META_INTERFACE(INTERFACE)                               \
+    static const ::android::String16 descriptor;                        \
+    static ::android::sp<I##INTERFACE> asInterface(                     \
+            const ::android::sp<::android::IBinder>& obj);              \
+    virtual const ::android::String16& getInterfaceDescriptor() const;  \
+    I##INTERFACE();                                                     \
+    virtual ~I##INTERFACE();                                            \
+
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
+    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
+    const ::android::String16&                                          \
+            I##INTERFACE::getInterfaceDescriptor() const {              \
+        return I##INTERFACE::descriptor;                                \
+    }                                                                   \
+    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
+            const ::android::sp<::android::IBinder>& obj)               \
+    {                                                                   \
+        ::android::sp<I##INTERFACE> intr;                               \
+        if (obj != NULL) {                                              \
+            intr = static_cast<I##INTERFACE*>(                          \
+                obj->queryLocalInterface(                               \
+                        I##INTERFACE::descriptor).get());               \
+            if (intr == NULL) {                                         \
+                intr = new Bp##INTERFACE(obj);                          \
+            }                                                           \
+        }                                                               \
+        return intr;                                                    \
+    }                                                                   \
+    I##INTERFACE::I##INTERFACE() { }                                    \
+    I##INTERFACE::~I##INTERFACE() { }                                   \
+
+
+#define CHECK_INTERFACE(interface, data, reply)                         \
+    if (!(data).checkInterface(this)) { return PERMISSION_DENIED; }     \
+
+
+// ----------------------------------------------------------------------
+// No user-serviceable parts after this...
+
+template<typename INTERFACE>
+inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
+        const String16& _descriptor)
+{
+    if (_descriptor == INTERFACE::descriptor) return this;
+    return NULL;
+}
+
+template<typename INTERFACE>
+inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
+{
+    return INTERFACE::getInterfaceDescriptor();
+}
+
+template<typename INTERFACE>
+IBinder* BnInterface<INTERFACE>::onAsBinder()
+{
+    return this;
+}
+
+template<typename INTERFACE>
+inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
+    : BpRefBase(remote)
+{
+}
+
+template<typename INTERFACE>
+inline IBinder* BpInterface<INTERFACE>::onAsBinder()
+{
+    return remote();
+}
+    
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
new file mode 100644
index 0000000..b21047f
--- /dev/null
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IMediaResourceMonitor : public IInterface {
+public:
+    DECLARE_META_INTERFACE(MediaResourceMonitor)
+
+    // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
+    enum {
+        TYPE_VIDEO_CODEC = 0,
+        TYPE_AUDIO_CODEC = 1,
+    };
+
+    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
+
+    enum {
+        NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+            uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
new file mode 100644
index 0000000..15a104f
--- /dev/null
+++ b/libs/binder/include/binder/IMemory.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMEMORY_H
+#define ANDROID_IMEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IMemoryHeap : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(MemoryHeap)
+
+    // flags returned by getFlags()
+    enum {
+        READ_ONLY   = 0x00000001
+    };
+
+    virtual int         getHeapID() const = 0;
+    virtual void*       getBase() const = 0;
+    virtual size_t      getSize() const = 0;
+    virtual uint32_t    getFlags() const = 0;
+    virtual uint32_t    getOffset() const = 0;
+
+    // these are there just for backward source compatibility
+    int32_t heapID() const { return getHeapID(); }
+    void*   base() const  { return getBase(); }
+    size_t  virtualSize() const { return getSize(); }
+};
+
+class BnMemoryHeap : public BnInterface<IMemoryHeap>
+{
+public:
+    virtual status_t onTransact( 
+            uint32_t code,
+            const Parcel& data,
+            Parcel* reply,
+            uint32_t flags = 0);
+    
+    BnMemoryHeap();
+protected:
+    virtual ~BnMemoryHeap();
+};
+
+// ----------------------------------------------------------------------------
+
+class IMemory : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(Memory)
+
+    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
+
+    // helpers
+    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
+    void* pointer() const;
+    size_t size() const;
+    ssize_t offset() const;
+};
+
+class BnMemory : public BnInterface<IMemory>
+{
+public:
+    virtual status_t onTransact(
+            uint32_t code,
+            const Parcel& data,
+            Parcel* reply,
+            uint32_t flags = 0);
+
+    BnMemory();
+protected:
+    virtual ~BnMemory();
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IMEMORY_H
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
new file mode 100644
index 0000000..245607e
--- /dev/null
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IPC_THREAD_STATE_H
+#define ANDROID_IPC_THREAD_STATE_H
+
+#include <utils/Errors.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <utils/Vector.h>
+
+#if defined(_WIN32)
+typedef  int  uid_t;
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState
+{
+public:
+    static  IPCThreadState*     self();
+    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
+    
+            sp<ProcessState>    process();
+            
+            status_t            clearLastError();
+
+            pid_t               getCallingPid() const;
+            uid_t               getCallingUid() const;
+
+            void                setStrictModePolicy(int32_t policy);
+            int32_t             getStrictModePolicy() const;
+
+            void                setLastTransactionBinderFlags(int32_t flags);
+            int32_t             getLastTransactionBinderFlags() const;
+
+            int64_t             clearCallingIdentity();
+            void                restoreCallingIdentity(int64_t token);
+            
+            int                 setupPolling(int* fd);
+            status_t            handlePolledCommands();
+            void                flushCommands();
+
+            void                joinThreadPool(bool isMain = true);
+            
+            // Stop the local process.
+            void                stopProcess(bool immediate = true);
+            
+            status_t            transact(int32_t handle,
+                                         uint32_t code, const Parcel& data,
+                                         Parcel* reply, uint32_t flags);
+
+            void                incStrongHandle(int32_t handle);
+            void                decStrongHandle(int32_t handle);
+            void                incWeakHandle(int32_t handle);
+            void                decWeakHandle(int32_t handle);
+            status_t            attemptIncStrongHandle(int32_t handle);
+    static  void                expungeHandle(int32_t handle, IBinder* binder);
+            status_t            requestDeathNotification(   int32_t handle,
+                                                            BpBinder* proxy); 
+            status_t            clearDeathNotification( int32_t handle,
+                                                        BpBinder* proxy); 
+
+    static  void                shutdown();
+
+    // Call this to disable switching threads to background scheduling when
+    // receiving incoming IPC calls.  This is specifically here for the
+    // Android system process, since it expects to have background apps calling
+    // in to it but doesn't want to acquire locks in its services while in
+    // the background.
+    static  void                disableBackgroundScheduling(bool disable);
+            bool                backgroundSchedulingDisabled();
+
+            // Call blocks until the number of executing binder threads is less than
+            // the maximum number of binder threads threads allowed for this process.
+            void                blockUntilThreadAvailable();
+
+private:
+                                IPCThreadState();
+                                ~IPCThreadState();
+
+            status_t            sendReply(const Parcel& reply, uint32_t flags);
+            status_t            waitForResponse(Parcel *reply,
+                                                status_t *acquireResult=NULL);
+            status_t            talkWithDriver(bool doReceive=true);
+            status_t            writeTransactionData(int32_t cmd,
+                                                     uint32_t binderFlags,
+                                                     int32_t handle,
+                                                     uint32_t code,
+                                                     const Parcel& data,
+                                                     status_t* statusBuffer);
+            status_t            getAndExecuteCommand();
+            status_t            executeCommand(int32_t command);
+            void                processPendingDerefs();
+
+            void                clearCaller();
+
+    static  void                threadDestructor(void *st);
+    static  void                freeBuffer(Parcel* parcel,
+                                           const uint8_t* data, size_t dataSize,
+                                           const binder_size_t* objects, size_t objectsSize,
+                                           void* cookie);
+    
+    const   sp<ProcessState>    mProcess;
+            Vector<BBinder*>    mPendingStrongDerefs;
+            Vector<RefBase::weakref_type*> mPendingWeakDerefs;
+
+            Parcel              mIn;
+            Parcel              mOut;
+            status_t            mLastError;
+            pid_t               mCallingPid;
+            uid_t               mCallingUid;
+            int32_t             mStrictModePolicy;
+            int32_t             mLastTransactionBinderFlags;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
new file mode 100644
index 0000000..25f3431
--- /dev/null
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_IPERMISSION_CONTROLLER_H
+#define ANDROID_IPERMISSION_CONTROLLER_H
+
+#include <binder/IInterface.h>
+#include <stdlib.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IPermissionController : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(PermissionController)
+
+    virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) = 0;
+
+    virtual void getPackagesForUid(const uid_t uid, Vector<String16> &packages) = 0;
+
+    virtual bool isRuntimePermission(const String16& permission) = 0;
+
+    enum {
+        CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1,
+        IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnPermissionController : public BnInterface<IPermissionController>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPERMISSION_CONTROLLER_H
+
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
new file mode 100644
index 0000000..2669f91
--- /dev/null
+++ b/libs/binder/include/binder/IProcessInfoService.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_I_PROCESS_INFO_SERVICE_H
+#define ANDROID_I_PROCESS_INFO_SERVICE_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IProcessInfoService : public IInterface {
+public:
+    DECLARE_META_INTERFACE(ProcessInfoService)
+
+    virtual status_t    getProcessStatesFromPids( size_t length,
+                                                  /*in*/ int32_t* pids,
+                                                  /*out*/ int32_t* states) = 0;
+
+    virtual status_t    getProcessStatesAndOomScoresFromPids( size_t length,
+                                                  /*in*/ int32_t* pids,
+                                                  /*out*/ int32_t* states,
+                                                  /*out*/ int32_t* scores) = 0;
+
+    enum {
+        GET_PROCESS_STATES_FROM_PIDS = IBinder::FIRST_CALL_TRANSACTION,
+        GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS,
+    };
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
new file mode 100644
index 0000000..e494fba
--- /dev/null
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_IRESULT_RECEIVER_H
+#define ANDROID_IRESULT_RECEIVER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IResultReceiver : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ResultReceiver)
+
+    virtual void send(int32_t resultCode) = 0;
+
+    enum {
+        OP_SEND = IBinder::FIRST_CALL_TRANSACTION
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnResultReceiver : public BnInterface<IResultReceiver>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IRESULT_RECEIVER_H
+
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
new file mode 100644
index 0000000..3b23f81
--- /dev/null
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_ISERVICE_MANAGER_H
+#define ANDROID_ISERVICE_MANAGER_H
+
+#include <binder/IInterface.h>
+#include <binder/IPermissionController.h>
+#include <utils/Vector.h>
+#include <utils/String16.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IServiceManager : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ServiceManager)
+
+    /**
+     * Retrieve an existing service, blocking for a few seconds
+     * if it doesn't yet exist.
+     */
+    virtual sp<IBinder>         getService( const String16& name) const = 0;
+
+    /**
+     * Retrieve an existing service, non-blocking.
+     */
+    virtual sp<IBinder>         checkService( const String16& name) const = 0;
+
+    /**
+     * Register a service.
+     */
+    virtual status_t            addService( const String16& name,
+                                            const sp<IBinder>& service,
+                                            bool allowIsolated = false) = 0;
+
+    /**
+     * Return list of all existing services.
+     */
+    virtual Vector<String16>    listServices() = 0;
+
+    enum {
+        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        CHECK_SERVICE_TRANSACTION,
+        ADD_SERVICE_TRANSACTION,
+        LIST_SERVICES_TRANSACTION,
+    };
+};
+
+sp<IServiceManager> defaultServiceManager();
+
+template<typename INTERFACE>
+status_t getService(const String16& name, sp<INTERFACE>* outService)
+{
+    const sp<IServiceManager> sm = defaultServiceManager();
+    if (sm != NULL) {
+        *outService = interface_cast<INTERFACE>(sm->getService(name));
+        if ((*outService) != NULL) return NO_ERROR;
+    }
+    return NAME_NOT_FOUND;
+}
+
+bool checkCallingPermission(const String16& permission);
+bool checkCallingPermission(const String16& permission,
+                            int32_t* outPid, int32_t* outUid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+
+}; // namespace android
+
+#endif // ANDROID_ISERVICE_MANAGER_H
+
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
new file mode 100644
index 0000000..fda9ee6
--- /dev/null
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+#ifndef ANDROID_ISHELL_CALLBACK_H
+#define ANDROID_ISHELL_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IShellCallback : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ShellCallback);
+
+    virtual int openOutputFile(const String16& path, const String16& seLinuxContext) = 0;
+
+    enum {
+        OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION
+    };
+};
+
+// ----------------------------------------------------------------------
+
+class BnShellCallback : public BnInterface<IShellCallback>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_ISHELL_CALLBACK_H
+
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
new file mode 100644
index 0000000..96ebaac
--- /dev/null
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IP_PREFIX_H
+#define ANDROID_IP_PREFIX_H
+
+#include <netinet/in.h>
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace net {
+
+/*
+ * C++ implementation of the Java class android.net.IpPrefix
+ */
+class IpPrefix : public Parcelable {
+public:
+    IpPrefix() = default;
+    virtual ~IpPrefix() = default;
+    IpPrefix(const IpPrefix& prefix) = default;
+
+    IpPrefix(const struct in6_addr& addr, int32_t plen):
+        mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
+
+    IpPrefix(const struct in_addr& addr, int32_t plen):
+        mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
+
+    bool getAddressAsIn6Addr(struct in6_addr* addr) const;
+    bool getAddressAsInAddr(struct in_addr* addr) const;
+
+    const struct in6_addr& getAddressAsIn6Addr() const;
+    const struct in_addr& getAddressAsInAddr() const;
+
+    bool isIpv6() const;
+    bool isIpv4() const;
+
+    int32_t getPrefixLength() const;
+
+    void setAddress(const struct in6_addr& addr);
+    void setAddress(const struct in_addr& addr);
+
+    void setPrefixLength(int32_t prefix);
+
+    friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
+
+    friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
+        return !(lhs == rhs);
+    }
+
+public:
+    // Overrides
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+private:
+    union InternalUnion {
+        InternalUnion() = default;
+        InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { };
+        InternalUnion(const struct in_addr &addr):mInAddr(addr) { };
+        struct in6_addr mIn6Addr;
+        struct in_addr mInAddr;
+    } mUnion;
+    int32_t mPrefixLength;
+    bool mIsIpv6;
+};
+
+}  // namespace net
+
+}  // namespace android
+
+#endif  // ANDROID_IP_PREFIX_H
diff --git a/libs/binder/include/binder/Map.h b/libs/binder/include/binder/Map.h
new file mode 100644
index 0000000..96a4f8a
--- /dev/null
+++ b/libs/binder/include/binder/Map.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MAP_H
+#define ANDROID_MAP_H
+
+#include <map>
+#include <string>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace binder {
+
+class Value;
+
+/**
+ * Convenience typedef for ::std::map<::std::string,::android::binder::Value>
+ */
+typedef ::std::map<::std::string, Value> Map;
+
+} // namespace binder
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_MAP_H
diff --git a/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
similarity index 100%
rename from include/binder/MemoryBase.h
rename to libs/binder/include/binder/MemoryBase.h
diff --git a/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
similarity index 100%
rename from include/binder/MemoryDealer.h
rename to libs/binder/include/binder/MemoryDealer.h
diff --git a/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
similarity index 100%
rename from include/binder/MemoryHeapBase.h
rename to libs/binder/include/binder/MemoryHeapBase.h
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
new file mode 100644
index 0000000..5d36526
--- /dev/null
+++ b/libs/binder/include/binder/Parcel.h
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PARCEL_H
+#define ANDROID_PARCEL_H
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Flattenable.h>
+#include <linux/android/binder.h>
+
+#include <binder/IInterface.h>
+#include <binder/Parcelable.h>
+#include <binder/Map.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template <typename T> class Flattenable;
+template <typename T> class LightFlattenable;
+class IBinder;
+class IPCThreadState;
+class ProcessState;
+class String8;
+class TextOutput;
+
+namespace binder {
+class Value;
+};
+
+class Parcel {
+    friend class IPCThreadState;
+public:
+    class ReadableBlob;
+    class WritableBlob;
+
+                        Parcel();
+                        ~Parcel();
+    
+    const uint8_t*      data() const;
+    size_t              dataSize() const;
+    size_t              dataAvail() const;
+    size_t              dataPosition() const;
+    size_t              dataCapacity() const;
+
+    status_t            setDataSize(size_t size);
+    void                setDataPosition(size_t pos) const;
+    status_t            setDataCapacity(size_t size);
+    
+    status_t            setData(const uint8_t* buffer, size_t len);
+
+    status_t            appendFrom(const Parcel *parcel,
+                                   size_t start, size_t len);
+
+    int                 compareData(const Parcel& other);
+
+    bool                allowFds() const;
+    bool                pushAllowFds(bool allowFds);
+    void                restoreAllowFds(bool lastValue);
+
+    bool                hasFileDescriptors() const;
+
+    // Writes the RPC header.
+    status_t            writeInterfaceToken(const String16& interface);
+
+    // Parses the RPC header, returning true if the interface name
+    // in the header matches the expected interface from the caller.
+    //
+    // Additionally, enforceInterface does part of the work of
+    // propagating the StrictMode policy mask, populating the current
+    // IPCThreadState, which as an optimization may optionally be
+    // passed in.
+    bool                enforceInterface(const String16& interface,
+                                         IPCThreadState* threadState = NULL) const;
+    bool                checkInterface(IBinder*) const;
+
+    void                freeData();
+
+private:
+    const binder_size_t* objects() const;
+
+public:
+    size_t              objectsCount() const;
+    
+    status_t            errorCheck() const;
+    void                setError(status_t err);
+    
+    status_t            write(const void* data, size_t len);
+    void*               writeInplace(size_t len);
+    status_t            writeUnpadded(const void* data, size_t len);
+    status_t            writeInt32(int32_t val);
+    status_t            writeUint32(uint32_t val);
+    status_t            writeInt64(int64_t val);
+    status_t            writeUint64(uint64_t val);
+    status_t            writeFloat(float val);
+    status_t            writeDouble(double val);
+    status_t            writeCString(const char* str);
+    status_t            writeString8(const String8& str);
+    status_t            writeString16(const String16& str);
+    status_t            writeString16(const std::unique_ptr<String16>& str);
+    status_t            writeString16(const char16_t* str, size_t len);
+    status_t            writeStrongBinder(const sp<IBinder>& val);
+    status_t            writeWeakBinder(const wp<IBinder>& val);
+    status_t            writeInt32Array(size_t len, const int32_t *val);
+    status_t            writeByteArray(size_t len, const uint8_t *val);
+    status_t            writeBool(bool val);
+    status_t            writeChar(char16_t val);
+    status_t            writeByte(int8_t val);
+
+    // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
+    status_t            writeUtf8AsUtf16(const std::string& str);
+    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+
+    status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
+    status_t            writeByteVector(const std::vector<int8_t>& val);
+    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
+    status_t            writeByteVector(const std::vector<uint8_t>& val);
+    status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
+    status_t            writeInt32Vector(const std::vector<int32_t>& val);
+    status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
+    status_t            writeInt64Vector(const std::vector<int64_t>& val);
+    status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
+    status_t            writeFloatVector(const std::vector<float>& val);
+    status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
+    status_t            writeDoubleVector(const std::vector<double>& val);
+    status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
+    status_t            writeBoolVector(const std::vector<bool>& val);
+    status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
+    status_t            writeCharVector(const std::vector<char16_t>& val);
+    status_t            writeString16Vector(
+                            const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
+    status_t            writeString16Vector(const std::vector<String16>& val);
+    status_t            writeUtf8VectorAsUtf16Vector(
+                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+    status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
+
+    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
+    status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
+
+    template<typename T>
+    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
+    template<typename T>
+    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
+    template<typename T>
+    status_t            writeParcelableVector(const std::vector<T>& val);
+
+    template<typename T>
+    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable);
+
+    status_t            writeParcelable(const Parcelable& parcelable);
+
+    status_t            writeValue(const binder::Value& value);
+
+    template<typename T>
+    status_t            write(const Flattenable<T>& val);
+
+    template<typename T>
+    status_t            write(const LightFlattenable<T>& val);
+
+    template<typename T>
+    status_t            writeVectorSize(const std::vector<T>& val);
+    template<typename T>
+    status_t            writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
+
+    status_t            writeMap(const binder::Map& map);
+    status_t            writeNullableMap(const std::unique_ptr<binder::Map>& map);
+
+    // Place a native_handle into the parcel (the native_handle's file-
+    // descriptors are dup'ed, so it is safe to delete the native_handle
+    // when this function returns).
+    // Doesn't take ownership of the native_handle.
+    status_t            writeNativeHandle(const native_handle* handle);
+
+    // Place a file descriptor into the parcel.  The given fd must remain
+    // valid for the lifetime of the parcel.
+    // The Parcel does not take ownership of the given fd unless you ask it to.
+    status_t            writeFileDescriptor(int fd, bool takeOwnership = false);
+
+    // Place a file descriptor into the parcel.  A dup of the fd is made, which
+    // will be closed once the parcel is destroyed.
+    status_t            writeDupFileDescriptor(int fd);
+
+    // Place a Java "parcel file descriptor" into the parcel.  The given fd must remain
+    // valid for the lifetime of the parcel.
+    // The Parcel does not take ownership of the given fd unless you ask it to.
+    status_t            writeParcelFileDescriptor(int fd, bool takeOwnership = false);
+
+    // Place a file descriptor into the parcel.  This will not affect the
+    // semantics of the smart file descriptor. A new descriptor will be
+    // created, and will be closed when the parcel is destroyed.
+    status_t            writeUniqueFileDescriptor(
+                            const base::unique_fd& fd);
+
+    // Place a vector of file desciptors into the parcel. Each descriptor is
+    // dup'd as in writeDupFileDescriptor
+    status_t            writeUniqueFileDescriptorVector(
+                            const std::unique_ptr<std::vector<base::unique_fd>>& val);
+    status_t            writeUniqueFileDescriptorVector(
+                            const std::vector<base::unique_fd>& val);
+
+    // Writes a blob to the parcel.
+    // If the blob is small, then it is stored in-place, otherwise it is
+    // transferred by way of an anonymous shared memory region.  Prefer sending
+    // immutable blobs if possible since they may be subsequently transferred between
+    // processes without further copying whereas mutable blobs always need to be copied.
+    // The caller should call release() on the blob after writing its contents.
+    status_t            writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
+
+    // Write an existing immutable blob file descriptor to the parcel.
+    // This allows the client to send the same blob to multiple processes
+    // as long as it keeps a dup of the blob file descriptor handy for later.
+    status_t            writeDupImmutableBlobFileDescriptor(int fd);
+
+    status_t            writeObject(const flat_binder_object& val, bool nullMetaData);
+
+    // Like Parcel.java's writeNoException().  Just writes a zero int32.
+    // Currently the native implementation doesn't do any of the StrictMode
+    // stack gathering and serialization that the Java implementation does.
+    status_t            writeNoException();
+
+    void                remove(size_t start, size_t amt);
+    
+    status_t            read(void* outData, size_t len) const;
+    const void*         readInplace(size_t len) const;
+    int32_t             readInt32() const;
+    status_t            readInt32(int32_t *pArg) const;
+    uint32_t            readUint32() const;
+    status_t            readUint32(uint32_t *pArg) const;
+    int64_t             readInt64() const;
+    status_t            readInt64(int64_t *pArg) const;
+    uint64_t            readUint64() const;
+    status_t            readUint64(uint64_t *pArg) const;
+    float               readFloat() const;
+    status_t            readFloat(float *pArg) const;
+    double              readDouble() const;
+    status_t            readDouble(double *pArg) const;
+    intptr_t            readIntPtr() const;
+    status_t            readIntPtr(intptr_t *pArg) const;
+    bool                readBool() const;
+    status_t            readBool(bool *pArg) const;
+    char16_t            readChar() const;
+    status_t            readChar(char16_t *pArg) const;
+    int8_t              readByte() const;
+    status_t            readByte(int8_t *pArg) const;
+
+    // Read a UTF16 encoded string, convert to UTF8
+    status_t            readUtf8FromUtf16(std::string* str) const;
+    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+
+    const char*         readCString() const;
+    String8             readString8() const;
+    status_t            readString8(String8* pArg) const;
+    String16            readString16() const;
+    status_t            readString16(String16* pArg) const;
+    status_t            readString16(std::unique_ptr<String16>* pArg) const;
+    const char16_t*     readString16Inplace(size_t* outLen) const;
+    sp<IBinder>         readStrongBinder() const;
+    status_t            readStrongBinder(sp<IBinder>* val) const;
+    status_t            readNullableStrongBinder(sp<IBinder>* val) const;
+    wp<IBinder>         readWeakBinder() const;
+
+    template<typename T>
+    status_t            readParcelableVector(
+                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
+    template<typename T>
+    status_t            readParcelableVector(std::vector<T>* val) const;
+
+    status_t            readParcelable(Parcelable* parcelable) const;
+
+    template<typename T>
+    status_t            readParcelable(std::unique_ptr<T>* parcelable) const;
+
+    status_t            readValue(binder::Value* value) const;
+
+    template<typename T>
+    status_t            readStrongBinder(sp<T>* val) const;
+
+    template<typename T>
+    status_t            readNullableStrongBinder(sp<T>* val) const;
+
+    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
+    status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+
+    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
+    status_t            readByteVector(std::vector<int8_t>* val) const;
+    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
+    status_t            readByteVector(std::vector<uint8_t>* val) const;
+    status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
+    status_t            readInt32Vector(std::vector<int32_t>* val) const;
+    status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
+    status_t            readInt64Vector(std::vector<int64_t>* val) const;
+    status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
+    status_t            readFloatVector(std::vector<float>* val) const;
+    status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
+    status_t            readDoubleVector(std::vector<double>* val) const;
+    status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
+    status_t            readBoolVector(std::vector<bool>* val) const;
+    status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
+    status_t            readCharVector(std::vector<char16_t>* val) const;
+    status_t            readString16Vector(
+                            std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
+    status_t            readString16Vector(std::vector<String16>* val) const;
+    status_t            readUtf8VectorFromUtf16Vector(
+                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+    status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
+
+    template<typename T>
+    status_t            read(Flattenable<T>& val) const;
+
+    template<typename T>
+    status_t            read(LightFlattenable<T>& val) const;
+
+    template<typename T>
+    status_t            resizeOutVector(std::vector<T>* val) const;
+    template<typename T>
+    status_t            resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+
+    status_t            readMap(binder::Map* map)const;
+    status_t            readNullableMap(std::unique_ptr<binder::Map>* map) const;
+
+    // Like Parcel.java's readExceptionCode().  Reads the first int32
+    // off of a Parcel's header, returning 0 or the negative error
+    // code on exceptions, but also deals with skipping over rich
+    // response headers.  Callers should use this to read & parse the
+    // response headers rather than doing it by hand.
+    int32_t             readExceptionCode() const;
+
+    // Retrieve native_handle from the parcel. This returns a copy of the
+    // parcel's native_handle (the caller takes ownership). The caller
+    // must free the native_handle with native_handle_close() and 
+    // native_handle_delete().
+    native_handle*     readNativeHandle() const;
+
+    
+    // Retrieve a file descriptor from the parcel.  This returns the raw fd
+    // in the parcel, which you do not own -- use dup() to get your own copy.
+    int                 readFileDescriptor() const;
+
+    // Retrieve a Java "parcel file descriptor" from the parcel.  This returns the raw fd
+    // in the parcel, which you do not own -- use dup() to get your own copy.
+    int                 readParcelFileDescriptor() const;
+
+    // Retrieve a smart file descriptor from the parcel.
+    status_t            readUniqueFileDescriptor(
+                            base::unique_fd* val) const;
+
+
+    // Retrieve a vector of smart file descriptors from the parcel.
+    status_t            readUniqueFileDescriptorVector(
+                            std::unique_ptr<std::vector<base::unique_fd>>* val) const;
+    status_t            readUniqueFileDescriptorVector(
+                            std::vector<base::unique_fd>* val) const;
+
+    // Reads a blob from the parcel.
+    // The caller should call release() on the blob after reading its contents.
+    status_t            readBlob(size_t len, ReadableBlob* outBlob) const;
+
+    const flat_binder_object* readObject(bool nullMetaData) const;
+
+    // Explicitly close all file descriptors in the parcel.
+    void                closeFileDescriptors();
+
+    // Debugging: get metrics on current allocations.
+    static size_t       getGlobalAllocSize();
+    static size_t       getGlobalAllocCount();
+
+private:
+    typedef void        (*release_func)(Parcel* parcel,
+                                        const uint8_t* data, size_t dataSize,
+                                        const binder_size_t* objects, size_t objectsSize,
+                                        void* cookie);
+                        
+    uintptr_t           ipcData() const;
+    size_t              ipcDataSize() const;
+    uintptr_t           ipcObjects() const;
+    size_t              ipcObjectsCount() const;
+    void                ipcSetDataReference(const uint8_t* data, size_t dataSize,
+                                            const binder_size_t* objects, size_t objectsCount,
+                                            release_func relFunc, void* relCookie);
+    
+public:
+    void                print(TextOutput& to, uint32_t flags = 0) const;
+
+private:
+                        Parcel(const Parcel& o);
+    Parcel&             operator=(const Parcel& o);
+    
+    status_t            finishWrite(size_t len);
+    void                releaseObjects();
+    void                acquireObjects();
+    status_t            growData(size_t len);
+    status_t            restartWrite(size_t desired);
+    status_t            continueWrite(size_t desired);
+    status_t            writePointer(uintptr_t val);
+    status_t            readPointer(uintptr_t *pArg) const;
+    uintptr_t           readPointer() const;
+    void                freeDataNoInit();
+    void                initState();
+    void                scanForFds() const;
+                        
+    template<class T>
+    status_t            readAligned(T *pArg) const;
+
+    template<class T>   T readAligned() const;
+
+    template<class T>
+    status_t            writeAligned(T val);
+
+    status_t            writeRawNullableParcelable(const Parcelable*
+                                                   parcelable);
+
+    template<typename T, typename U>
+    status_t            unsafeReadTypedVector(std::vector<T>* val,
+                                              status_t(Parcel::*read_func)(U*) const) const;
+    template<typename T>
+    status_t            readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
+                                                status_t(Parcel::*read_func)(T*) const) const;
+    template<typename T>
+    status_t            readTypedVector(std::vector<T>* val,
+                                        status_t(Parcel::*read_func)(T*) const) const;
+    template<typename T, typename U>
+    status_t            unsafeWriteTypedVector(const std::vector<T>& val,
+                                               status_t(Parcel::*write_func)(U));
+    template<typename T>
+    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+                                                 status_t(Parcel::*write_func)(const T&));
+    template<typename T>
+    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+                                                 status_t(Parcel::*write_func)(T));
+    template<typename T>
+    status_t            writeTypedVector(const std::vector<T>& val,
+                                         status_t(Parcel::*write_func)(const T&));
+    template<typename T>
+    status_t            writeTypedVector(const std::vector<T>& val,
+                                         status_t(Parcel::*write_func)(T));
+
+    status_t            mError;
+    uint8_t*            mData;
+    size_t              mDataSize;
+    size_t              mDataCapacity;
+    mutable size_t      mDataPos;
+    binder_size_t*      mObjects;
+    size_t              mObjectsSize;
+    size_t              mObjectsCapacity;
+    mutable size_t      mNextObjectHint;
+
+    mutable bool        mFdsKnown;
+    mutable bool        mHasFds;
+    bool                mAllowFds;
+
+    release_func        mOwner;
+    void*               mOwnerCookie;
+
+    class Blob {
+    public:
+        Blob();
+        ~Blob();
+
+        void clear();
+        void release();
+        inline size_t size() const { return mSize; }
+        inline int fd() const { return mFd; }
+        inline bool isMutable() const { return mMutable; }
+
+    protected:
+        void init(int fd, void* data, size_t size, bool isMutable);
+
+        int mFd; // owned by parcel so not closed when released
+        void* mData;
+        size_t mSize;
+        bool mMutable;
+    };
+
+    #if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wweak-vtables"
+    #endif
+
+    // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects
+    // following Flattenable template/protocol.
+    class FlattenableHelperInterface {
+    protected:
+        ~FlattenableHelperInterface() { }
+    public:
+        virtual size_t getFlattenedSize() const = 0;
+        virtual size_t getFdCount() const = 0;
+        virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0;
+        virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0;
+    };
+
+    #if defined(__clang__)
+    #pragma clang diagnostic pop
+    #endif
+
+    // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the
+    // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time
+    // protocol.
+    template<typename T>
+    class FlattenableHelper : public FlattenableHelperInterface {
+        friend class Parcel;
+        const Flattenable<T>& val;
+        explicit FlattenableHelper(const Flattenable<T>& _val) : val(_val) { }
+
+    protected:
+        ~FlattenableHelper() = default;
+    public:
+        virtual size_t getFlattenedSize() const {
+            return val.getFlattenedSize();
+        }
+        virtual size_t getFdCount() const {
+            return val.getFdCount();
+        }
+        virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const {
+            return val.flatten(buffer, size, fds, count);
+        }
+        virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) {
+            return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count);
+        }
+    };
+    status_t write(const FlattenableHelperInterface& val);
+    status_t read(FlattenableHelperInterface& val) const;
+
+public:
+    class ReadableBlob : public Blob {
+        friend class Parcel;
+    public:
+        inline const void* data() const { return mData; }
+        inline void* mutableData() { return isMutable() ? mData : NULL; }
+    };
+
+    class WritableBlob : public Blob {
+        friend class Parcel;
+    public:
+        inline void* data() { return mData; }
+    };
+
+private:
+    size_t mOpenAshmemSize;
+
+public:
+    // TODO: Remove once ABI can be changed.
+    size_t getBlobAshmemSize() const;
+    size_t getOpenAshmemSize() const;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename T>
+status_t Parcel::write(const Flattenable<T>& val) {
+    const FlattenableHelper<T> helper(val);
+    return write(helper);
+}
+
+template<typename T>
+status_t Parcel::write(const LightFlattenable<T>& val) {
+    size_t size(val.getFlattenedSize());
+    if (!val.isFixedSize()) {
+        if (size > INT32_MAX) {
+            return BAD_VALUE;
+        }
+        status_t err = writeInt32(static_cast<int32_t>(size));
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    if (size) {
+        void* buffer = writeInplace(size);
+        if (buffer == NULL)
+            return NO_MEMORY;
+        return val.flatten(buffer, size);
+    }
+    return NO_ERROR;
+}
+
+template<typename T>
+status_t Parcel::read(Flattenable<T>& val) const {
+    FlattenableHelper<T> helper(val);
+    return read(helper);
+}
+
+template<typename T>
+status_t Parcel::read(LightFlattenable<T>& val) const {
+    size_t size;
+    if (val.isFixedSize()) {
+        size = val.getFlattenedSize();
+    } else {
+        int32_t s;
+        status_t err = readInt32(&s);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        size = static_cast<size_t>(s);
+    }
+    if (size) {
+        void const* buffer = readInplace(size);
+        return buffer == NULL ? NO_MEMORY :
+                val.unflatten(buffer, size);
+    }
+    return NO_ERROR;
+}
+
+template<typename T>
+status_t Parcel::writeVectorSize(const std::vector<T>& val) {
+    if (val.size() > INT32_MAX) {
+        return BAD_VALUE;
+    }
+    return writeInt32(static_cast<int32_t>(val.size()));
+}
+
+template<typename T>
+status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) {
+    if (!val) {
+        return writeInt32(-1);
+    }
+
+    return writeVectorSize(*val);
+}
+
+template<typename T>
+status_t Parcel::resizeOutVector(std::vector<T>* val) const {
+    int32_t size;
+    status_t err = readInt32(&size);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (size < 0) {
+        return UNEXPECTED_NULL;
+    }
+    val->resize(size_t(size));
+    return OK;
+}
+
+template<typename T>
+status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
+    int32_t size;
+    status_t err = readInt32(&size);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    val->reset();
+    if (size >= 0) {
+        val->reset(new std::vector<T>(size_t(size)));
+    }
+
+    return OK;
+}
+
+template<typename T>
+status_t Parcel::readStrongBinder(sp<T>* val) const {
+    sp<IBinder> tmp;
+    status_t ret = readStrongBinder(&tmp);
+
+    if (ret == OK) {
+        *val = interface_cast<T>(tmp);
+
+        if (val->get() == nullptr) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return ret;
+}
+
+template<typename T>
+status_t Parcel::readNullableStrongBinder(sp<T>* val) const {
+    sp<IBinder> tmp;
+    status_t ret = readNullableStrongBinder(&tmp);
+
+    if (ret == OK) {
+        *val = interface_cast<T>(tmp);
+
+        if (val->get() == nullptr && tmp.get() != nullptr) {
+            ret = UNKNOWN_ERROR;
+        }
+    }
+
+    return ret;
+}
+
+template<typename T, typename U>
+status_t Parcel::unsafeReadTypedVector(
+        std::vector<T>* val,
+        status_t(Parcel::*read_func)(U*) const) const {
+    int32_t size;
+    status_t status = this->readInt32(&size);
+
+    if (status != OK) {
+        return status;
+    }
+
+    if (size < 0) {
+        return UNEXPECTED_NULL;
+    }
+
+    if (val->max_size() < static_cast<size_t>(size)) {
+        return NO_MEMORY;
+    }
+
+    val->resize(static_cast<size_t>(size));
+
+    if (val->size() < static_cast<size_t>(size)) {
+        return NO_MEMORY;
+    }
+
+    for (auto& v: *val) {
+        status = (this->*read_func)(&v);
+
+        if (status != OK) {
+            return status;
+        }
+    }
+
+    return OK;
+}
+
+template<typename T>
+status_t Parcel::readTypedVector(std::vector<T>* val,
+                                 status_t(Parcel::*read_func)(T*) const) const {
+    return unsafeReadTypedVector(val, read_func);
+}
+
+template<typename T>
+status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
+                                         status_t(Parcel::*read_func)(T*) const) const {
+    const size_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    val->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    val->reset(new std::vector<T>());
+
+    status = unsafeReadTypedVector(val->get(), read_func);
+
+    if (status != OK) {
+        val->reset();
+    }
+
+    return status;
+}
+
+template<typename T, typename U>
+status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val,
+                                        status_t(Parcel::*write_func)(U)) {
+    if (val.size() > std::numeric_limits<int32_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    status_t status = this->writeInt32(static_cast<int32_t>(val.size()));
+
+    if (status != OK) {
+        return status;
+    }
+
+    for (const auto& item : val) {
+        status = (this->*write_func)(item);
+
+        if (status != OK) {
+            return status;
+        }
+    }
+
+    return OK;
+}
+
+template<typename T>
+status_t Parcel::writeTypedVector(const std::vector<T>& val,
+                                  status_t(Parcel::*write_func)(const T&)) {
+    return unsafeWriteTypedVector(val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeTypedVector(const std::vector<T>& val,
+                                  status_t(Parcel::*write_func)(T)) {
+    return unsafeWriteTypedVector(val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+                                          status_t(Parcel::*write_func)(const T&)) {
+    if (val.get() == nullptr) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
+                                          status_t(Parcel::*write_func)(T)) {
+    if (val.get() == nullptr) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
+status_t Parcel::readParcelableVector(std::vector<T>* val) const {
+    return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable);
+}
+
+template<typename T>
+status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
+    const size_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    val->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    val->reset(new std::vector<std::unique_ptr<T>>());
+
+    status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
+
+    if (status != OK) {
+        val->reset();
+    }
+
+    return status;
+}
+
+template<typename T>
+status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
+    const size_t start = dataPosition();
+    int32_t present;
+    status_t status = readInt32(&present);
+    parcelable->reset();
+
+    if (status != OK || !present) {
+        return status;
+    }
+
+    setDataPosition(start);
+    parcelable->reset(new T());
+
+    status = readParcelable(parcelable->get());
+
+    if (status != OK) {
+        parcelable->reset();
+    }
+
+    return status;
+}
+
+template<typename T>
+status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
+    return writeRawNullableParcelable(parcelable.get());
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::vector<T>& val) {
+    return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable);
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
+    if (val.get() == nullptr) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) {
+    if (val.get() == nullptr) {
+        return this->writeInt32(-1);
+    }
+
+    return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+}
+
+// ---------------------------------------------------------------------------
+
+inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
+{
+    parcel.print(to);
+    return to;
+}
+
+// ---------------------------------------------------------------------------
+
+// Generic acquire and release of objects.
+void acquire_object(const sp<ProcessState>& proc,
+                    const flat_binder_object& obj, const void* who);
+void release_object(const sp<ProcessState>& proc,
+                    const flat_binder_object& obj, const void* who);
+
+void flatten_binder(const sp<ProcessState>& proc,
+                    const sp<IBinder>& binder, flat_binder_object* out);
+void flatten_binder(const sp<ProcessState>& proc,
+                    const wp<IBinder>& binder, flat_binder_object* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+                          const flat_binder_object& flat, sp<IBinder>* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+                          const flat_binder_object& flat, wp<IBinder>* out);
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PARCEL_H
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
new file mode 100644
index 0000000..d5b57ac
--- /dev/null
+++ b/libs/binder/include/binder/Parcelable.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PARCELABLE_H
+#define ANDROID_PARCELABLE_H
+
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class Parcel;
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// Abstract interface of all parcelables.
+class Parcelable {
+public:
+    virtual ~Parcelable() = default;
+
+    // Write |this| parcelable to the given |parcel|.  Keep in mind that
+    // implementations of writeToParcel must be manually kept in sync
+    // with readFromParcel and the Java equivalent versions of these methods.
+    //
+    // Returns android::OK on success and an appropriate error otherwise.
+    virtual status_t writeToParcel(Parcel* parcel) const = 0;
+
+    // Read data from the given |parcel| into |this|.  After readFromParcel
+    // completes, |this| should have equivalent state to the object that
+    // wrote itself to the parcel.
+    //
+    // Returns android::OK on success and an appropriate error otherwise.
+    virtual status_t readFromParcel(const Parcel* parcel) = 0;
+};  // class Parcelable
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+}  // namespace android
+
+#endif // ANDROID_PARCELABLE_H
diff --git a/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
similarity index 100%
rename from include/binder/PermissionCache.h
rename to libs/binder/include/binder/PermissionCache.h
diff --git a/libs/binder/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
new file mode 100644
index 0000000..322fef9
--- /dev/null
+++ b/libs/binder/include/binder/PersistableBundle.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PERSISTABLE_BUNDLE_H
+#define ANDROID_PERSISTABLE_BUNDLE_H
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace os {
+
+/*
+ * C++ implementation of PersistableBundle, a mapping from String values to
+ * various types that can be saved to persistent and later restored.
+ */
+class PersistableBundle : public Parcelable {
+public:
+    PersistableBundle() = default;
+    virtual ~PersistableBundle() = default;
+    PersistableBundle(const PersistableBundle& bundle) = default;
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    bool empty() const;
+    size_t size() const;
+    size_t erase(const String16& key);
+
+    /*
+     * Setters for PersistableBundle. Adds a a key-value pair instantiated with
+     * |key| and |value| into the member map appropriate for the type of |value|.
+     * If there is already an existing value for |key|, |value| will replace it.
+     */
+    void putBoolean(const String16& key, bool value);
+    void putInt(const String16& key, int32_t value);
+    void putLong(const String16& key, int64_t value);
+    void putDouble(const String16& key, double value);
+    void putString(const String16& key, const String16& value);
+    void putBooleanVector(const String16& key, const std::vector<bool>& value);
+    void putIntVector(const String16& key, const std::vector<int32_t>& value);
+    void putLongVector(const String16& key, const std::vector<int64_t>& value);
+    void putDoubleVector(const String16& key, const std::vector<double>& value);
+    void putStringVector(const String16& key, const std::vector<String16>& value);
+    void putPersistableBundle(const String16& key, const PersistableBundle& value);
+
+    /*
+     * Getters for PersistableBundle. If |key| exists, these methods write the
+     * value associated with |key| into |out|, and return true. Otherwise, these
+     * methods return false.
+     */
+    bool getBoolean(const String16& key, bool* out) const;
+    bool getInt(const String16& key, int32_t* out) const;
+    bool getLong(const String16& key, int64_t* out) const;
+    bool getDouble(const String16& key, double* out) const;
+    bool getString(const String16& key, String16* out) const;
+    bool getBooleanVector(const String16& key, std::vector<bool>* out) const;
+    bool getIntVector(const String16& key, std::vector<int32_t>* out) const;
+    bool getLongVector(const String16& key, std::vector<int64_t>* out) const;
+    bool getDoubleVector(const String16& key, std::vector<double>* out) const;
+    bool getStringVector(const String16& key, std::vector<String16>* out) const;
+    bool getPersistableBundle(const String16& key, PersistableBundle* out) const;
+
+    /* Getters for all keys for each value type */
+    std::set<String16> getBooleanKeys() const;
+    std::set<String16> getIntKeys() const;
+    std::set<String16> getLongKeys() const;
+    std::set<String16> getDoubleKeys() const;
+    std::set<String16> getStringKeys() const;
+    std::set<String16> getBooleanVectorKeys() const;
+    std::set<String16> getIntVectorKeys() const;
+    std::set<String16> getLongVectorKeys() const;
+    std::set<String16> getDoubleVectorKeys() const;
+    std::set<String16> getStringVectorKeys() const;
+    std::set<String16> getPersistableBundleKeys() const;
+
+    friend bool operator==(const PersistableBundle& lhs, const PersistableBundle& rhs) {
+        return (lhs.mBoolMap == rhs.mBoolMap && lhs.mIntMap == rhs.mIntMap &&
+                lhs.mLongMap == rhs.mLongMap && lhs.mDoubleMap == rhs.mDoubleMap &&
+                lhs.mStringMap == rhs.mStringMap && lhs.mBoolVectorMap == rhs.mBoolVectorMap &&
+                lhs.mIntVectorMap == rhs.mIntVectorMap &&
+                lhs.mLongVectorMap == rhs.mLongVectorMap &&
+                lhs.mDoubleVectorMap == rhs.mDoubleVectorMap &&
+                lhs.mStringVectorMap == rhs.mStringVectorMap &&
+                lhs.mPersistableBundleMap == rhs.mPersistableBundleMap);
+    }
+
+    friend bool operator!=(const PersistableBundle& lhs, const PersistableBundle& rhs) {
+        return !(lhs == rhs);
+    }
+
+private:
+    status_t writeToParcelInner(Parcel* parcel) const;
+    status_t readFromParcelInner(const Parcel* parcel, size_t length);
+
+    std::map<String16, bool> mBoolMap;
+    std::map<String16, int32_t> mIntMap;
+    std::map<String16, int64_t> mLongMap;
+    std::map<String16, double> mDoubleMap;
+    std::map<String16, String16> mStringMap;
+    std::map<String16, std::vector<bool>> mBoolVectorMap;
+    std::map<String16, std::vector<int32_t>> mIntVectorMap;
+    std::map<String16, std::vector<int64_t>> mLongVectorMap;
+    std::map<String16, std::vector<double>> mDoubleVectorMap;
+    std::map<String16, std::vector<String16>> mStringVectorMap;
+    std::map<String16, PersistableBundle> mPersistableBundleMap;
+};
+
+}  // namespace os
+
+}  // namespace android
+
+#endif  // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
new file mode 100644
index 0000000..0da61ee
--- /dev/null
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PROCESS_INFO_SERVICE_H
+#define ANDROID_PROCESS_INFO_SERVICE_H
+
+#include <binder/IProcessInfoService.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+#include <sys/types.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class ProcessInfoService : public Singleton<ProcessInfoService> {
+
+    friend class Singleton<ProcessInfoService>;
+    sp<IProcessInfoService> mProcessInfoService;
+    Mutex mProcessInfoLock;
+
+    ProcessInfoService();
+
+    status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
+    status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids,
+            /*out*/ int32_t* states, /*out*/ int32_t *scores);
+    void updateBinderLocked();
+
+    static const int BINDER_ATTEMPT_LIMIT = 5;
+
+public:
+
+    /**
+     * For each PID in the given "pids" input array, write the current process state
+     * for that process into the "states" output array, or
+     * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+     * exists.
+     *
+     * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+     */
+    static status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids,
+            /*out*/ int32_t* states) {
+        return ProcessInfoService::getInstance().getProcessStatesImpl(length, /*in*/ pids,
+                /*out*/ states);
+    }
+
+    /**
+     * For each PID in the given "pids" input array, write the current process state
+     * for that process into the "states" output array, or
+     * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+     * exists. OoM scores will also be written in the "scores" output array.
+     * Please also note that clients calling this method need to have
+     * "GET_PROCESS_STATE_AND_OOM_SCORE" permission.
+     *
+     * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+     */
+    static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids,
+            /*out*/ int32_t* states, /*out*/ int32_t *scores) {
+        return ProcessInfoService::getInstance().getProcessStatesScoresImpl(
+                length, /*in*/ pids, /*out*/ states, /*out*/ scores);
+    }
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_INFO_SERVICE_H
+
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
new file mode 100644
index 0000000..1ef045d
--- /dev/null
+++ b/libs/binder/include/binder/ProcessState.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PROCESS_STATE_H
+#define ANDROID_PROCESS_STATE_H
+
+#include <binder/IBinder.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include <utils/threads.h>
+
+#include <pthread.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState;
+
+class ProcessState : public virtual RefBase
+{
+public:
+    static  sp<ProcessState>    self();
+    /* 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.
+     */
+    static  sp<ProcessState>    initWithDriver(const char *driver);
+
+            void                setContextObject(const sp<IBinder>& object);
+            sp<IBinder>         getContextObject(const sp<IBinder>& caller);
+        
+            void                setContextObject(const sp<IBinder>& object,
+                                                 const String16& name);
+            sp<IBinder>         getContextObject(const String16& name,
+                                                 const sp<IBinder>& caller);
+
+            void                startThreadPool();
+                        
+    typedef bool (*context_check_func)(const String16& name,
+                                       const sp<IBinder>& caller,
+                                       void* userData);
+        
+            bool                isContextManager(void) const;
+            bool                becomeContextManager(
+                                    context_check_func checkFunc,
+                                    void* userData);
+
+            sp<IBinder>         getStrongProxyForHandle(int32_t handle);
+            wp<IBinder>         getWeakProxyForHandle(int32_t handle);
+            void                expungeHandle(int32_t handle, IBinder* binder);
+
+            void                spawnPooledThread(bool isMain);
+            
+            status_t            setThreadPoolMaxThreadCount(size_t maxThreads);
+            void                giveThreadPoolName();
+
+            String8             getDriverName();
+
+private:
+    friend class IPCThreadState;
+    
+                                ProcessState(const char* driver);
+                                ~ProcessState();
+
+                                ProcessState(const ProcessState& o);
+            ProcessState&       operator=(const ProcessState& o);
+            String8             makeBinderThreadName();
+
+            struct handle_entry {
+                IBinder* binder;
+                RefBase::weakref_type* refs;
+            };
+
+            handle_entry*       lookupHandleLocked(int32_t handle);
+
+            String8             mDriverName;
+            int                 mDriverFD;
+            void*               mVMStart;
+
+            // Protects thread count variable below.
+            pthread_mutex_t     mThreadCountLock;
+            pthread_cond_t      mThreadCountDecrement;
+            // Number of binder threads current executing a command.
+            size_t              mExecutingThreadsCount;
+            // Maximum number for binder threads allowed for this process.
+            size_t              mMaxThreads;
+            // Time when thread pool was emptied
+            int64_t             mStarvationStartTimeMs;
+
+    mutable Mutex               mLock;  // protects everything below.
+
+            Vector<handle_entry>mHandleToObject;
+
+            bool                mManagesContexts;
+            context_check_func  mBinderContextCheckFunc;
+            void*               mBinderContextUserData;
+
+            KeyedVector<String16, sp<IBinder> >
+                                mContexts;
+
+
+            String8             mRootDir;
+            bool                mThreadPoolStarted;
+    volatile int32_t            mThreadPoolSeq;
+};
+    
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PROCESS_STATE_H
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
new file mode 100644
index 0000000..3bfd462
--- /dev/null
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <cutils/compiler.h>
+
+// Set to 1 to enable CallStacks when logging errors
+#define SI_DUMP_CALLSTACKS 0
+#if SI_DUMP_CALLSTACKS
+#include <utils/CallStack.h>
+#endif
+
+#include <utils/NativeHandle.h>
+
+#include <functional>
+#include <type_traits>
+
+namespace android {
+namespace SafeInterface {
+
+// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
+class ParcelHandler {
+public:
+    explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {}
+
+    // Specializations for types with dedicated handling in Parcel
+    status_t read(const Parcel& parcel, bool* b) const {
+        return callParcel("readBool", [&]() { return parcel.readBool(b); });
+    }
+    status_t write(Parcel* parcel, bool b) const {
+        return callParcel("writeBool", [&]() { return parcel->writeBool(b); });
+    }
+    template <typename E>
+    typename std::enable_if<std::is_enum<E>::value, status_t>::type read(const Parcel& parcel,
+                                                                         E* e) const {
+        typename std::underlying_type<E>::type u{};
+        status_t result = read(parcel, &u);
+        *e = static_cast<E>(u);
+        return result;
+    }
+    template <typename E>
+    typename std::enable_if<std::is_enum<E>::value, status_t>::type write(Parcel* parcel,
+                                                                          E e) const {
+        return write(parcel, static_cast<typename std::underlying_type<E>::type>(e));
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+            const Parcel& parcel, T* t) const {
+        return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+            Parcel* parcel, const T& t) const {
+        return callParcel("write(Flattenable)", [&]() { return parcel->write(t); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+            const Parcel& parcel, sp<T>* t) const {
+        *t = new T{};
+        return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+            Parcel* parcel, const sp<T>& t) const {
+        return callParcel("write(sp<Flattenable>)", [&]() { return parcel->write(*(t.get())); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type read(
+            const Parcel& parcel, T* t) const {
+        return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type write(
+            Parcel* parcel, const T& t) const {
+        return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); });
+    }
+    template <typename NH>
+    typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type read(
+            const Parcel& parcel, NH* nh) {
+        *nh = NativeHandle::create(parcel.readNativeHandle(), true);
+        return NO_ERROR;
+    }
+    template <typename NH>
+    typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type write(
+            Parcel* parcel, const NH& nh) {
+        return callParcel("write(sp<NativeHandle>)",
+                          [&]() { return parcel->writeNativeHandle(nh->handle()); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+            const Parcel& parcel, T* t) const {
+        return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+            Parcel* parcel, const T& t) const {
+        return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); });
+    }
+    status_t read(const Parcel& parcel, String8* str) const {
+        return callParcel("readString8", [&]() { return parcel.readString8(str); });
+    }
+    status_t write(Parcel* parcel, const String8& str) const {
+        return callParcel("writeString8", [&]() { return parcel->writeString8(str); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type read(
+            const Parcel& parcel, sp<T>* pointer) const {
+        return callParcel("readNullableStrongBinder",
+                          [&]() { return parcel.readNullableStrongBinder(pointer); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type write(
+            Parcel* parcel, const sp<T>& pointer) const {
+        return callParcel("writeStrongBinder",
+                          [&]() { return parcel->writeStrongBinder(pointer); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type read(
+            const Parcel& parcel, sp<T>* pointer) const {
+        return callParcel("readNullableStrongBinder[IInterface]",
+                          [&]() { return parcel.readNullableStrongBinder(pointer); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type write(
+            Parcel* parcel, const sp<T>& interface) const {
+        return write(parcel, IInterface::asBinder(interface));
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+            const Parcel& parcel, std::vector<T>* v) const {
+        return callParcel("readParcelableVector", [&]() { return parcel.readParcelableVector(v); });
+    }
+    template <typename T>
+    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+            Parcel* parcel, const std::vector<T>& v) const {
+        return callParcel("writeParcelableVector",
+                          [&]() { return parcel->writeParcelableVector(v); });
+    }
+
+    // Templates to handle integral types. We use a struct template to require that the called
+    // function exactly matches the signedness and size of the argument (e.g., the argument isn't
+    // silently widened).
+    template <bool isSigned, size_t size, typename I>
+    struct HandleInt;
+    template <typename I>
+    struct HandleInt<true, 4, I> {
+        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+            return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); });
+        }
+        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+            return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); });
+        }
+    };
+    template <typename I>
+    struct HandleInt<false, 4, I> {
+        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+            return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); });
+        }
+        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+            return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); });
+        }
+    };
+    template <typename I>
+    struct HandleInt<true, 8, I> {
+        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+            return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); });
+        }
+        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+            return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); });
+        }
+    };
+    template <typename I>
+    struct HandleInt<false, 8, I> {
+        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+            return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); });
+        }
+        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+            return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); });
+        }
+    };
+    template <typename I>
+    typename std::enable_if<std::is_integral<I>::value, status_t>::type read(const Parcel& parcel,
+                                                                             I* i) const {
+        return HandleInt<std::is_signed<I>::value, sizeof(I), I>::read(*this, parcel, i);
+    }
+    template <typename I>
+    typename std::enable_if<std::is_integral<I>::value, status_t>::type write(Parcel* parcel,
+                                                                              I i) const {
+        return HandleInt<std::is_signed<I>::value, sizeof(I), I>::write(*this, parcel, i);
+    }
+
+private:
+    const char* const mLogTag;
+
+    // Helper to encapsulate error handling while calling the various Parcel methods
+    template <typename Function>
+    status_t callParcel(const char* name, Function f) const {
+        status_t error = f();
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error));
+#if SI_DUMP_CALLSTACKS
+            CallStack callStack(mLogTag);
+#endif
+        }
+        return error;
+    }
+};
+
+// Utility struct template which allows us to retrieve the types of the parameters of a member
+// function pointer
+template <typename T>
+struct ParamExtractor;
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...)> {
+    using ParamTuple = std::tuple<Params...>;
+};
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...) const> {
+    using ParamTuple = std::tuple<Params...>;
+};
+
+} // namespace SafeInterface
+
+template <typename Interface>
+class SafeBpInterface : public BpInterface<Interface> {
+protected:
+    SafeBpInterface(const sp<IBinder>& impl, const char* logTag)
+          : BpInterface<Interface>(impl), mLogTag(logTag) {}
+    ~SafeBpInterface() override = default;
+
+    // callRemote is used to invoke a synchronous procedure call over Binder
+    template <typename Method, typename TagType, typename... Args>
+    status_t callRemote(TagType tag, Args&&... args) const {
+        static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+        // Verify that the arguments are compatible with the parameters
+        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+        static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+                      "Invalid argument type");
+
+        // Write the input arguments to the data Parcel
+        Parcel data;
+        data.writeInterfaceToken(this->getInterfaceDescriptor());
+
+        status_t error = writeInputs(&data, std::forward<Args>(args)...);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by writeInputs
+            return error;
+        }
+
+        // Send the data Parcel to the remote and retrieve the reply parcel
+        Parcel reply;
+        error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+            CallStack callStack(mLogTag);
+#endif
+            return error;
+        }
+
+        // Read the outputs from the reply Parcel into the output arguments
+        error = readOutputs(reply, std::forward<Args>(args)...);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by readOutputs
+            return error;
+        }
+
+        // Retrieve the result code from the reply Parcel
+        status_t result = NO_ERROR;
+        error = reply.readInt32(&result);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            ALOG(LOG_ERROR, mLogTag, "Failed to obtain result");
+#if SI_DUMP_CALLSTACKS
+            CallStack callStack(mLogTag);
+#endif
+            return error;
+        }
+        return result;
+    }
+
+    // callRemoteAsync is used to invoke an asynchronous procedure call over Binder
+    template <typename Method, typename TagType, typename... Args>
+    void callRemoteAsync(TagType tag, Args&&... args) const {
+        static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+        // Verify that the arguments are compatible with the parameters
+        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+        static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+                      "Invalid argument type");
+
+        // Write the input arguments to the data Parcel
+        Parcel data;
+        data.writeInterfaceToken(this->getInterfaceDescriptor());
+        status_t error = writeInputs(&data, std::forward<Args>(args)...);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by writeInputs
+            return;
+        }
+
+        // There will be no data in the reply Parcel since the call is one-way
+        Parcel reply;
+        error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply,
+                                         IBinder::FLAG_ONEWAY);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+            CallStack callStack(mLogTag);
+#endif
+        }
+    }
+
+private:
+    const char* const mLogTag;
+
+    // This struct provides information on whether the decayed types of the elements at Index in the
+    // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references,
+    // and a few other less common operations) are the same
+    template <size_t Index, typename T, typename U>
+    struct DecayedElementsMatch {
+    private:
+        using FirstT = typename std::tuple_element<Index, T>::type;
+        using DecayedT = typename std::decay<FirstT>::type;
+        using FirstU = typename std::tuple_element<Index, U>::type;
+        using DecayedU = typename std::decay<FirstU>::type;
+
+    public:
+        static constexpr bool value = std::is_same<DecayedT, DecayedU>::value;
+    };
+
+    // When comparing whether the argument types match the parameter types, we first decay them (see
+    // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are
+    // equivalent enough for our purposes
+    template <typename T, typename U>
+    struct ArgsMatchParams {};
+    template <typename... Args, typename... Params>
+    struct ArgsMatchParams<std::tuple<Args...>, std::tuple<Params...>> {
+        static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments");
+        static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments");
+
+    private:
+        template <size_t Index>
+        static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type
+        elementsMatch() {
+            if (!DecayedElementsMatch<Index, std::tuple<Args...>, std::tuple<Params...>>::value) {
+                return false;
+            }
+            return elementsMatch<Index + 1>();
+        }
+        template <size_t Index>
+        static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type
+        elementsMatch() {
+            return true;
+        }
+
+    public:
+        static constexpr bool value = elementsMatch<0>();
+    };
+
+    // Since we assume that pointer arguments are outputs, we can use this template struct to
+    // determine whether or not a given argument is fundamentally a pointer type and thus an output
+    template <typename T>
+    struct IsPointerIfDecayed {
+    private:
+        using Decayed = typename std::decay<T>::type;
+
+    public:
+        static constexpr bool value = std::is_pointer<Decayed>::value;
+    };
+
+    template <typename T>
+    typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+            Parcel* data, T&& t) const {
+        return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward<T>(t));
+    }
+    template <typename T>
+    typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+            Parcel* /*data*/, T&& /*t*/) const {
+        return NO_ERROR;
+    }
+
+    // This method iterates through all of the arguments, writing them to the data Parcel if they
+    // are an input (i.e., if they are not a pointer type)
+    template <typename T, typename... Remaining>
+    status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const {
+        status_t error = writeIfInput(data, std::forward<T>(t));
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by writeIfInput
+            return error;
+        }
+        return writeInputs(data, std::forward<Remaining>(remaining)...);
+    }
+    static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; }
+
+    template <typename T>
+    typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+            const Parcel& reply, T&& t) const {
+        return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward<T>(t));
+    }
+    template <typename T>
+    static typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+            const Parcel& /*reply*/, T&& /*t*/) {
+        return NO_ERROR;
+    }
+
+    // Similar to writeInputs except that it reads output arguments from the reply Parcel
+    template <typename T, typename... Remaining>
+    status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const {
+        status_t error = readIfOutput(reply, std::forward<T>(t));
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by readIfOutput
+            return error;
+        }
+        return readOutputs(reply, std::forward<Remaining>(remaining)...);
+    }
+    static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; }
+};
+
+template <typename Interface>
+class SafeBnInterface : public BnInterface<Interface> {
+public:
+    explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {}
+
+protected:
+    template <typename Method>
+    status_t callLocal(const Parcel& data, Parcel* reply, Method method) {
+        CHECK_INTERFACE(this, data, reply);
+
+        // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+        // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+        // outputs. When we ultimately call into the method, we will pass the addresses of the
+        // output arguments instead of their tuple members directly, but the storage will live in
+        // the tuple.
+        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+        typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+        // Read the inputs from the data Parcel into the argument tuple
+        status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by read
+            return error;
+        }
+
+        // Call the local method
+        status_t result = MethodCaller<ParamTuple>::call(this, method, &rawArgs);
+
+        // Extract the outputs from the argument tuple and write them into the reply Parcel
+        error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by write
+            return error;
+        }
+
+        // Return the result code in the reply Parcel
+        error = reply->writeInt32(result);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            ALOG(LOG_ERROR, mLogTag, "Failed to write result");
+#if SI_DUMP_CALLSTACKS
+            CallStack callStack(mLogTag);
+#endif
+            return error;
+        }
+        return NO_ERROR;
+    }
+
+    template <typename Method>
+    status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) {
+        // reply is not actually used by CHECK_INTERFACE
+        CHECK_INTERFACE(this, data, reply);
+
+        // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+        // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+        // outputs. When we ultimately call into the method, we will pass the addresses of the
+        // output arguments instead of their tuple members directly, but the storage will live in
+        // the tuple.
+        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+        typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+        // Read the inputs from the data Parcel into the argument tuple
+        status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+        if (CC_UNLIKELY(error != NO_ERROR)) {
+            // A message will have been logged by read
+            return error;
+        }
+
+        // Call the local method
+        MethodCaller<ParamTuple>::callVoid(this, method, &rawArgs);
+
+        // After calling, there is nothing more to do since asynchronous calls do not return a value
+        // to the caller
+        return NO_ERROR;
+    }
+
+private:
+    const char* const mLogTag;
+
+    // RemoveFirst strips the first element from a tuple.
+    // For example, given T = std::tuple<A, B, C>, RemoveFirst<T>::type = std::tuple<B, C>
+    template <typename T, typename... Args>
+    struct RemoveFirst;
+    template <typename T, typename... Args>
+    struct RemoveFirst<std::tuple<T, Args...>> {
+        using type = std::tuple<Args...>;
+    };
+
+    // RawConverter strips a tuple down to its fundamental types, discarding both pointers and
+    // references. This allows us to allocate storage for both input (non-pointer) arguments and
+    // output (pointer) arguments in one tuple.
+    // For example, given T = std::tuple<const A&, B*>, RawConverter<T>::type = std::tuple<A, B>
+    template <typename Unconverted, typename... Converted>
+    struct RawConverter;
+    template <typename Unconverted, typename... Converted>
+    struct RawConverter<std::tuple<Converted...>, Unconverted> {
+    private:
+        using ElementType = typename std::tuple_element<0, Unconverted>::type;
+        using Decayed = typename std::decay<ElementType>::type;
+        using WithoutPointer = typename std::remove_pointer<Decayed>::type;
+
+    public:
+        using type = typename RawConverter<std::tuple<Converted..., WithoutPointer>,
+                                           typename RemoveFirst<Unconverted>::type>::type;
+    };
+    template <typename... Converted>
+    struct RawConverter<std::tuple<Converted...>, std::tuple<>> {
+        using type = std::tuple<Converted...>;
+    };
+
+    // This provides a simple way to determine whether the indexed element of Args... is a pointer
+    template <size_t I, typename... Args>
+    struct ElementIsPointer {
+    private:
+        using ElementType = typename std::tuple_element<I, std::tuple<Args...>>::type;
+
+    public:
+        static constexpr bool value = std::is_pointer<ElementType>::value;
+    };
+
+    // This class iterates over the parameter types, and if a given parameter is an input
+    // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel
+    template <typename... Params>
+    class InputReader;
+    template <typename... Params>
+    class InputReader<std::tuple<Params...>> {
+    public:
+        explicit InputReader(const char* logTag) : mLogTag(logTag) {}
+
+        // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit
+        // index (starting with 0 here) instead of using recursion and stripping the first element.
+        // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are
+        // instead just using a tuple as a convenient container for variadic types, whereas here we
+        // can't modify the argument tuple without causing unnecessary copies or moves of the data
+        // contained therein.
+        template <typename RawTuple>
+        status_t readInputs(const Parcel& data, RawTuple* args) {
+            return dispatchArg<0>(data, args);
+        }
+
+    private:
+        const char* const mLogTag;
+
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+                const Parcel& data, RawTuple* args) {
+            return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get<I>(*args));
+        }
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+                const Parcel& /*data*/, RawTuple* /*args*/) {
+            return NO_ERROR;
+        }
+
+        // Recursively iterate through the arguments
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+                const Parcel& data, RawTuple* args) {
+            status_t error = readIfInput<I>(data, args);
+            if (CC_UNLIKELY(error != NO_ERROR)) {
+                // A message will have been logged in read
+                return error;
+            }
+            return dispatchArg<I + 1>(data, args);
+        }
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+                const Parcel& /*data*/, RawTuple* /*args*/) {
+            return NO_ERROR;
+        }
+    };
+
+    // getForCall uses the types of the parameters to determine whether a given element of the
+    // argument tuple is an input, which should be passed directly into the call, or an output, for
+    // which its address should be passed into the call
+    template <size_t I, typename RawTuple, typename... Params>
+    static typename std::enable_if<
+            ElementIsPointer<I, Params...>::value,
+            typename std::tuple_element<I, std::tuple<Params...>>::type>::type
+    getForCall(RawTuple* args) {
+        return &std::get<I>(*args);
+    }
+    template <size_t I, typename RawTuple, typename... Params>
+    static typename std::enable_if<
+            !ElementIsPointer<I, Params...>::value,
+            typename std::tuple_element<I, std::tuple<Params...>>::type>::type&
+    getForCall(RawTuple* args) {
+        return std::get<I>(*args);
+    }
+
+    // This template class uses std::index_sequence and parameter pack expansion to call the given
+    // method using the elements of the argument tuple (after those arguments are passed through
+    // getForCall to get addresses instead of values for output arguments)
+    template <typename... Params>
+    struct MethodCaller;
+    template <typename... Params>
+    struct MethodCaller<std::tuple<Params...>> {
+    public:
+        // The calls through these to the helper methods are necessary to generate the
+        // std::index_sequences used to unpack the argument tuple into the method call
+        template <typename Class, typename MemberFunction, typename RawTuple>
+        static status_t call(Class* instance, MemberFunction function, RawTuple* args) {
+            return callHelper(instance, function, args, std::index_sequence_for<Params...>{});
+        }
+        template <typename Class, typename MemberFunction, typename RawTuple>
+        static void callVoid(Class* instance, MemberFunction function, RawTuple* args) {
+            callVoidHelper(instance, function, args, std::index_sequence_for<Params...>{});
+        }
+
+    private:
+        template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+        static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args,
+                                   std::index_sequence<I...> /*unused*/) {
+            return (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+        }
+        template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+        static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args,
+                                   std::index_sequence<I...> /*unused*/) {
+            (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+        }
+    };
+
+    // This class iterates over the parameter types, and if a given parameter is an output
+    // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel
+    template <typename... Params>
+    struct OutputWriter;
+    template <typename... Params>
+    struct OutputWriter<std::tuple<Params...>> {
+    public:
+        explicit OutputWriter(const char* logTag) : mLogTag(logTag) {}
+
+        // See the note on InputReader::readInputs for why this differs from the arguably simpler
+        // RemoveFirst approach in SafeBpInterface
+        template <typename RawTuple>
+        status_t writeOutputs(Parcel* reply, RawTuple* args) {
+            return dispatchArg<0>(reply, args);
+        }
+
+    private:
+        const char* const mLogTag;
+
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type
+        writeIfOutput(Parcel* reply, RawTuple* args) {
+            return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get<I>(*args));
+        }
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type
+        writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) {
+            return NO_ERROR;
+        }
+
+        // Recursively iterate through the arguments
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+                Parcel* reply, RawTuple* args) {
+            status_t error = writeIfOutput<I>(reply, args);
+            if (CC_UNLIKELY(error != NO_ERROR)) {
+                // A message will have been logged in read
+                return error;
+            }
+            return dispatchArg<I + 1>(reply, args);
+        }
+        template <std::size_t I, typename RawTuple>
+        typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+                Parcel* /*reply*/, RawTuple* /*args*/) {
+            return NO_ERROR;
+        }
+    };
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
new file mode 100644
index 0000000..c3738f8
--- /dev/null
+++ b/libs/binder/include/binder/Status.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BINDER_STATUS_H
+#define ANDROID_BINDER_STATUS_H
+
+#include <cstdint>
+#include <sstream>
+
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace binder {
+
+// An object similar in function to a status_t except that it understands
+// how exceptions are encoded in the prefix of a Parcel. Used like:
+//
+//     Parcel data;
+//     Parcel reply;
+//     status_t status;
+//     binder::Status remote_exception;
+//     if ((status = data.writeInterfaceToken(interface_descriptor)) != OK ||
+//         (status = data.writeInt32(function_input)) != OK) {
+//         // We failed to write into the memory of our local parcel?
+//     }
+//     if ((status = remote()->transact(transaction, data, &reply)) != OK) {
+//        // Something has gone wrong in the binder driver or libbinder.
+//     }
+//     if ((status = remote_exception.readFromParcel(reply)) != OK) {
+//         // The remote didn't correctly write the exception header to the
+//         // reply.
+//     }
+//     if (!remote_exception.isOk()) {
+//         // The transaction went through correctly, but the remote reported an
+//         // exception during handling.
+//     }
+//
+class Status final {
+public:
+    // Keep the exception codes in sync with android/os/Parcel.java.
+    enum Exception {
+        EX_NONE = 0,
+        EX_SECURITY = -1,
+        EX_BAD_PARCELABLE = -2,
+        EX_ILLEGAL_ARGUMENT = -3,
+        EX_NULL_POINTER = -4,
+        EX_ILLEGAL_STATE = -5,
+        EX_NETWORK_MAIN_THREAD = -6,
+        EX_UNSUPPORTED_OPERATION = -7,
+        EX_SERVICE_SPECIFIC = -8,
+        EX_PARCELABLE = -9,
+
+        // This is special and Java specific; see Parcel.java.
+        EX_HAS_REPLY_HEADER = -128,
+        // This is special, and indicates to C++ binder proxies that the
+        // transaction has failed at a low level.
+        EX_TRANSACTION_FAILED = -129,
+    };
+
+    // A more readable alias for the default constructor.
+    static Status ok();
+
+    // Authors should explicitly pick whether their integer is:
+    //  - an exception code (EX_* above)
+    //  - service specific error code
+    //  - status_t
+    //
+    //  Prefer a generic exception code when possible, then a service specific
+    //  code, and finally a status_t for low level failures or legacy support.
+    //  Exception codes and service specific errors map to nicer exceptions for
+    //  Java clients.
+    static Status fromExceptionCode(int32_t exceptionCode);
+    static Status fromExceptionCode(int32_t exceptionCode,
+                                    const String8& message);
+    static Status fromExceptionCode(int32_t exceptionCode,
+                                    const char* message);
+
+    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
+    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+                                           const String8& message);
+    static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
+                                           const char* message);
+
+    static Status fromStatusT(status_t status);
+
+    Status() = default;
+    ~Status() = default;
+
+    // Status objects are copyable and contain just simple data.
+    Status(const Status& status) = default;
+    Status(Status&& status) = default;
+    Status& operator=(const Status& status) = default;
+
+    // Bear in mind that if the client or service is a Java endpoint, this
+    // is not the logic which will provide/interpret the data here.
+    status_t readFromParcel(const Parcel& parcel);
+    status_t writeToParcel(Parcel* parcel) const;
+
+    // Set one of the pre-defined exception types defined above.
+    void setException(int32_t ex, const String8& message);
+    // Set a service specific exception with error code.
+    void setServiceSpecificError(int32_t errorCode, const String8& message);
+    // Setting a |status| != OK causes generated code to return |status|
+    // from Binder transactions, rather than writing an exception into the
+    // reply Parcel.  This is the least preferable way of reporting errors.
+    void setFromStatusT(status_t status);
+
+    // Get information about an exception.
+    int32_t exceptionCode() const  { return mException; }
+    const String8& exceptionMessage() const { return mMessage; }
+    status_t transactionError() const {
+        return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK;
+    }
+    int32_t serviceSpecificErrorCode() const {
+        return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0;
+    }
+
+    bool isOk() const { return mException == EX_NONE; }
+
+    // For logging.
+    String8 toString8() const;
+
+private:
+    Status(int32_t exceptionCode, int32_t errorCode);
+    Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
+
+    // If |mException| == EX_TRANSACTION_FAILED, generated code will return
+    // |mErrorCode| as the result of the transaction rather than write an
+    // exception to the reply parcel.
+    //
+    // Otherwise, we always write |mException| to the parcel.
+    // If |mException| !=  EX_NONE, we write |mMessage| as well.
+    // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well.
+    int32_t mException = EX_NONE;
+    int32_t mErrorCode = 0;
+    String8 mMessage;
+};  // class Status
+
+// For gtest output logging
+std::stringstream& operator<< (std::stringstream& stream, const Status& s);
+
+}  // namespace binder
+}  // namespace android
+
+#endif // ANDROID_BINDER_STATUS_H
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
new file mode 100644
index 0000000..851e01f
--- /dev/null
+++ b/libs/binder/include/binder/TextOutput.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TEXTOUTPUT_H
+#define ANDROID_TEXTOUTPUT_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <sstream>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class TextOutput
+{
+public:
+                        TextOutput();
+    virtual             ~TextOutput();
+    
+    virtual status_t    print(const char* txt, size_t len) = 0;
+    virtual void        moveIndent(int delta) = 0;
+    
+    class Bundle {
+    public:
+        inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); }
+        inline ~Bundle() { mTO.popBundle(); }
+    private:
+        TextOutput&     mTO;
+    };
+    
+    virtual void        pushBundle() = 0;
+    virtual void        popBundle() = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+// Text output stream for printing to the log (via utils/Log.h).
+extern TextOutput& alog;
+
+// Text output stream for printing to stdout.
+extern TextOutput& aout;
+
+// Text output stream for printing to stderr.
+extern TextOutput& aerr;
+
+typedef TextOutput& (*TextOutputManipFunc)(TextOutput&);
+
+TextOutput& endl(TextOutput& to);
+TextOutput& indent(TextOutput& to);
+TextOutput& dedent(TextOutput& to);
+
+template<typename T>
+TextOutput& operator<<(TextOutput& to, const T& val)
+{
+    std::stringstream strbuf;
+    strbuf << val;
+    std::string str = strbuf.str();
+    to.print(str.c_str(), str.size());
+    return to;
+}
+
+TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
+
+class TypeCode
+{
+public:
+    inline TypeCode(uint32_t code);
+    inline ~TypeCode();
+
+    inline uint32_t typeCode() const;
+
+private:
+    uint32_t mCode;
+};
+
+TextOutput& operator<<(TextOutput& to, const TypeCode& val);
+
+class HexDump
+{
+public:
+    HexDump(const void *buf, size_t size, size_t bytesPerLine=16);
+    inline ~HexDump();
+    
+    inline HexDump& setBytesPerLine(size_t bytesPerLine);
+    inline HexDump& setSingleLineCutoff(int32_t bytes);
+    inline HexDump& setAlignment(size_t alignment);
+    inline HexDump& setCArrayStyle(bool enabled);
+    
+    inline const void* buffer() const;
+    inline size_t size() const;
+    inline size_t bytesPerLine() const;
+    inline int32_t singleLineCutoff() const;
+    inline size_t alignment() const;
+    inline bool carrayStyle() const;
+
+private:
+    const void* mBuffer;
+    size_t mSize;
+    size_t mBytesPerLine;
+    int32_t mSingleLineCutoff;
+    size_t mAlignment;
+    bool mCArrayStyle;
+};
+
+TextOutput& operator<<(TextOutput& to, const HexDump& val);
+inline TextOutput& operator<<(TextOutput& to,
+                              decltype(std::endl<char,
+                                       std::char_traits<char>>)
+                              /*val*/) {
+    endl(to);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const char &c)
+{
+    to.print(&c, 1);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const bool &val)
+{
+    if (val) to.print("true", 4);
+    else to.print("false", 5);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const String16& val)
+{
+    to << String8(val).string();
+    return to;
+}
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline TextOutput& endl(TextOutput& to)
+{
+    to.print("\n", 1);
+    return to;
+}
+
+inline TextOutput& indent(TextOutput& to)
+{
+    to.moveIndent(1);
+    return to;
+}
+
+inline TextOutput& dedent(TextOutput& to)
+{
+    to.moveIndent(-1);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
+{
+    return (*func)(to);
+}
+
+inline TypeCode::TypeCode(uint32_t code) : mCode(code) { }
+inline TypeCode::~TypeCode() { }
+inline uint32_t TypeCode::typeCode() const { return mCode; }
+
+inline HexDump::~HexDump() { }
+
+inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) {
+    mBytesPerLine = bytesPerLine; return *this;
+}
+inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) {
+    mSingleLineCutoff = bytes; return *this;
+}
+inline HexDump& HexDump::setAlignment(size_t alignment) {
+    mAlignment = alignment; return *this;
+}
+inline HexDump& HexDump::setCArrayStyle(bool enabled) {
+    mCArrayStyle = enabled; return *this;
+}
+
+inline const void* HexDump::buffer() const { return mBuffer; }
+inline size_t HexDump::size() const { return mSize; }
+inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; }
+inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; }
+inline size_t HexDump::alignment() const { return mAlignment; }
+inline bool HexDump::carrayStyle() const { return mCArrayStyle; }
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_TEXTOUTPUT_H
diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h
new file mode 100644
index 0000000..4dee3d8
--- /dev/null
+++ b/libs/binder/include/binder/Value.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VALUE_H
+#define ANDROID_VALUE_H
+
+#include <stdint.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <binder/Map.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Parcel;
+
+namespace binder {
+
+/**
+ * A limited C++ generic type. The purpose of this class is to allow C++
+ * programs to make use of (or implement) Binder interfaces which make use
+ * the Java "Object" generic type (either via the use of the Map type or
+ * some other mechanism).
+ *
+ * This class only supports a limited set of types, but additional types
+ * may be easily added to this class in the future as needed---without
+ * breaking binary compatability.
+ *
+ * This class was written in such a way as to help avoid type errors by
+ * giving each type their own explicity-named accessor methods (rather than
+ * overloaded methods).
+ *
+ * When reading or writing this class to a Parcel, use the `writeValue()`
+ * and `readValue()` methods.
+ */
+class Value {
+public:
+    Value();
+    virtual ~Value();
+
+    Value& swap(Value &);
+
+    bool empty() const;
+
+    void clear();
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+    const std::type_info& type() const;
+#endif
+
+    int32_t parcelType() const;
+
+    bool operator==(const Value& rhs) const;
+    bool operator!=(const Value& rhs) const { return !this->operator==(rhs); }
+
+    Value(const Value& value);
+    Value(const bool& value);
+    Value(const int8_t& value);
+    Value(const int32_t& value);
+    Value(const int64_t& value);
+    Value(const double& value);
+    Value(const String16& value);
+    Value(const std::vector<bool>& value);
+    Value(const std::vector<uint8_t>& value);
+    Value(const std::vector<int32_t>& value);
+    Value(const std::vector<int64_t>& value);
+    Value(const std::vector<double>& value);
+    Value(const std::vector<String16>& value);
+    Value(const os::PersistableBundle& value);
+    Value(const binder::Map& value);
+
+    Value& operator=(const Value& rhs);
+    Value& operator=(const int8_t& rhs);
+    Value& operator=(const bool& rhs);
+    Value& operator=(const int32_t& rhs);
+    Value& operator=(const int64_t& rhs);
+    Value& operator=(const double& rhs);
+    Value& operator=(const String16& rhs);
+    Value& operator=(const std::vector<bool>& rhs);
+    Value& operator=(const std::vector<uint8_t>& rhs);
+    Value& operator=(const std::vector<int32_t>& rhs);
+    Value& operator=(const std::vector<int64_t>& rhs);
+    Value& operator=(const std::vector<double>& rhs);
+    Value& operator=(const std::vector<String16>& rhs);
+    Value& operator=(const os::PersistableBundle& rhs);
+    Value& operator=(const binder::Map& rhs);
+
+    void putBoolean(const bool& value);
+    void putByte(const int8_t& value);
+    void putInt(const int32_t& value);
+    void putLong(const int64_t& value);
+    void putDouble(const double& value);
+    void putString(const String16& value);
+    void putBooleanVector(const std::vector<bool>& value);
+    void putByteVector(const std::vector<uint8_t>& value);
+    void putIntVector(const std::vector<int32_t>& value);
+    void putLongVector(const std::vector<int64_t>& value);
+    void putDoubleVector(const std::vector<double>& value);
+    void putStringVector(const std::vector<String16>& value);
+    void putPersistableBundle(const os::PersistableBundle& value);
+    void putMap(const binder::Map& value);
+
+    bool getBoolean(bool* out) const;
+    bool getByte(int8_t* out) const;
+    bool getInt(int32_t* out) const;
+    bool getLong(int64_t* out) const;
+    bool getDouble(double* out) const;
+    bool getString(String16* out) const;
+    bool getBooleanVector(std::vector<bool>* out) const;
+    bool getByteVector(std::vector<uint8_t>* out) const;
+    bool getIntVector(std::vector<int32_t>* out) const;
+    bool getLongVector(std::vector<int64_t>* out) const;
+    bool getDoubleVector(std::vector<double>* out) const;
+    bool getStringVector(std::vector<String16>* out) const;
+    bool getPersistableBundle(os::PersistableBundle* out) const;
+    bool getMap(binder::Map* out) const;
+
+    bool isBoolean() const;
+    bool isByte() const;
+    bool isInt() const;
+    bool isLong() const;
+    bool isDouble() const;
+    bool isString() const;
+    bool isBooleanVector() const;
+    bool isByteVector() const;
+    bool isIntVector() const;
+    bool isLongVector() const;
+    bool isDoubleVector() const;
+    bool isStringVector() const;
+    bool isPersistableBundle() const;
+    bool isMap() const;
+
+    // String Convenience Adapters
+    // ---------------------------
+
+    Value(const String8& value):               Value(String16(value)) { }
+    Value(const ::std::string& value):         Value(String8(value.c_str())) { }
+    void putString(const String8& value)       { return putString(String16(value)); }
+    void putString(const ::std::string& value) { return putString(String8(value.c_str())); }
+    Value& operator=(const String8& rhs)       { return *this = String16(rhs); }
+    Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); }
+    bool getString(String8* out) const;
+    bool getString(::std::string* out) const;
+
+private:
+
+    // This allows ::android::Parcel to call the two methods below.
+    friend class ::android::Parcel;
+
+    // This is called by ::android::Parcel::writeValue()
+    status_t writeToParcel(Parcel* parcel) const;
+
+    // This is called by ::android::Parcel::readValue()
+    status_t readFromParcel(const Parcel* parcel);
+
+    template<typename T> class Content;
+    class ContentBase;
+
+    ContentBase* mContent;
+};
+
+}  // namespace binder
+
+}  // namespace android
+
+#endif  // ANDROID_VALUE_H
diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/include/private/binder/ParcelValTypes.h
new file mode 100644
index 0000000..666d22a
--- /dev/null
+++ b/libs/binder/include/private/binder/ParcelValTypes.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace android {
+namespace binder {
+
+// Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
+enum {
+    VAL_NULL = -1,
+    VAL_STRING = 0,
+    VAL_INTEGER = 1,
+    VAL_MAP = 2,
+    VAL_BUNDLE = 3,
+    VAL_PARCELABLE = 4,
+    VAL_SHORT = 5,
+    VAL_LONG = 6,
+    VAL_DOUBLE = 8,
+    VAL_BOOLEAN = 9,
+    VAL_BYTEARRAY = 13,
+    VAL_STRINGARRAY = 14,
+    VAL_IBINDER = 15,
+    VAL_INTARRAY = 18,
+    VAL_LONGARRAY = 19,
+    VAL_BYTE = 20,
+    VAL_SERIALIZABLE = 21,
+    VAL_BOOLEANARRAY = 23,
+    VAL_PERSISTABLEBUNDLE = 25,
+    VAL_DOUBLEARRAY = 28,
+};
+
+} // namespace binder
+} // namespace android
diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/include/private/binder/Static.h
new file mode 100644
index 0000000..3d10456
--- /dev/null
+++ b/libs/binder/include/private/binder/Static.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <utils/threads.h>
+
+#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// For TextStream.cpp
+extern Vector<int32_t> gTextBuffers;
+
+// For ProcessState.cpp
+extern Mutex gProcessMutex;
+extern sp<ProcessState> gProcess;
+
+// For IServiceManager.cpp
+extern Mutex gDefaultServiceManagerLock;
+extern sp<IServiceManager> gDefaultServiceManager;
+extern sp<IPermissionController> gPermissionController;
+
+}   // namespace android
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
new file mode 100644
index 0000000..2f11622
--- /dev/null
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BINDER_MODULE_H_
+#define _BINDER_MODULE_H_
+
+#ifdef __cplusplus
+namespace android {
+#endif
+
+/* obtain structures and constants from the kernel header */
+
+#include <sys/ioctl.h>
+#include <linux/android/binder.h>
+
+#ifdef __cplusplus
+}   // namespace android
+#endif
+
+#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
new file mode 100644
index 0000000..3071408
--- /dev/null
+++ b/libs/binder/tests/Android.bp
@@ -0,0 +1,131 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "binderDriverInterfaceTest_IPC_32",
+    srcs: ["binderDriverInterfaceTest.cpp"],
+    compile_multilib: "32",
+    cflags: ["-DBINDER_IPC_32BIT=1"],
+}
+
+cc_test {
+    product_variables: {
+        binder32bit: {
+            cflags: ["-DBINDER_IPC_32BIT=1"],
+        },
+    },
+
+    name: "binderDriverInterfaceTest",
+    srcs: ["binderDriverInterfaceTest.cpp"],
+}
+
+cc_test {
+    name: "binderValueTypeTest",
+    srcs: ["binderValueTypeTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_test {
+    name: "binderLibTest_IPC_32",
+    srcs: ["binderLibTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    compile_multilib: "32",
+    cflags: ["-DBINDER_IPC_32BIT=1"],
+}
+
+cc_test {
+    product_variables: {
+        binder32bit: {
+            cflags: ["-DBINDER_IPC_32BIT=1"],
+        },
+    },
+
+    name: "binderLibTest",
+    srcs: ["binderLibTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_test {
+    name: "binderThroughputTest",
+    srcs: ["binderThroughputTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    clang: true,
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-sign-compare",
+        "-O3",
+    ],
+}
+
+cc_test {
+    name: "binderTextOutputTest",
+    srcs: ["binderTextOutputTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libbase",
+    ],
+}
+
+cc_test {
+    name: "schd-dbg",
+    srcs: ["schd-dbg.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libbase",
+    ],
+}
+
+cc_test {
+    name: "binderSafeInterfaceTest",
+    srcs: ["binderSafeInterfaceTest.cpp"],
+
+    cppflags: [
+        "-Werror",
+        "-Weverything",
+        "-Wno-c++98-compat",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-global-constructors",
+        "-Wno-padded",
+        "-Wno-weak-vtables",
+    ],
+
+    cpp_std: "experimental",
+    gnu_extensions: false,
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/libs/binder/tests/Android.mk b/libs/binder/tests/Android.mk
deleted file mode 100644
index a40523d..0000000
--- a/libs/binder/tests/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
-endif
-endif
-
-LOCAL_MODULE := binderDriverInterfaceTest
-LOCAL_SRC_FILES := binderDriverInterfaceTest.cpp
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := binderLibTest
-LOCAL_SRC_FILES := binderLibTest.cpp
-LOCAL_SHARED_LIBRARIES := libbinder libutils
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := binderThroughputTest
-LOCAL_SRC_FILES := binderThroughputTest.cpp
-LOCAL_SHARED_LIBRARIES := libbinder libutils
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -g -Wall -Werror -std=c++11 -Wno-missing-field-initializers -Wno-sign-compare -O3
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 0277550..ff5912f 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,7 +20,7 @@
 #include <stdlib.h>
 
 #include <gtest/gtest.h>
-#include <linux/binder.h>
+#include <linux/android/binder.h>
 #include <binder/IBinder.h>
 #include <sys/mman.h>
 #include <poll.h>
@@ -229,7 +229,9 @@
             .sender_euid = 0,
             .data_size = 0,
             .offsets_size = 0,
-            .data = {0, 0},
+            .data = {
+                .ptr = {0, 0},
+            },
         },
     };
     struct {
@@ -350,4 +352,3 @@
 
     return RUN_ALL_TESTS();
 }
-
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 3df3acf..a04869a 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -34,6 +34,7 @@
 
 static testing::Environment* binder_env;
 static char *binderservername;
+static char *binderserversuffix;
 static char binderserverarg[] = "--binderserver";
 
 static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -44,6 +45,7 @@
     BINDER_LIB_TEST_ADD_SERVER,
     BINDER_LIB_TEST_CALL_BACK,
     BINDER_LIB_TEST_NOP_CALL_BACK,
+    BINDER_LIB_TEST_GET_SELF_TRANSACTION,
     BINDER_LIB_TEST_GET_ID_TRANSACTION,
     BINDER_LIB_TEST_INDIRECT_TRANSACTION,
     BINDER_LIB_TEST_SET_ERROR_TRANSACTION,
@@ -55,6 +57,7 @@
     BINDER_LIB_TEST_EXIT_TRANSACTION,
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
+    BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
 };
 
 pid_t start_server_process(int arg2)
@@ -70,6 +73,7 @@
         binderserverarg,
         stri,
         strpipefd1,
+        binderserversuffix,
         NULL
     };
 
@@ -252,14 +256,10 @@
             int ret;
             pthread_mutex_lock(&m_waitMutex);
             if (!m_eventTriggered) {
-#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
-                pthread_cond_timeout_np(&m_waitCond, &m_waitMutex, timeout_s * 1000);
-#else
                 struct timespec ts;
                 clock_gettime(CLOCK_REALTIME, &ts);
                 ts.tv_sec += timeout_s;
                 pthread_cond_timedwait(&m_waitCond, &m_waitMutex, &ts);
-#endif
             }
             ret = m_eventTriggered ? NO_ERROR : TIMED_OUT;
             pthread_mutex_unlock(&m_waitMutex);
@@ -391,7 +391,7 @@
 
     ret = reply.readInt32(&count);
     ASSERT_EQ(NO_ERROR, ret);
-    EXPECT_EQ(ARRAY_SIZE(serverId), count);
+    EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
         BinderLibTestBundle replyi(&reply);
@@ -441,7 +441,7 @@
 
     ret = reply.readInt32(&count);
     ASSERT_EQ(NO_ERROR, ret);
-    EXPECT_EQ(ARRAY_SIZE(serverId), count);
+    EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
         int32_t counti;
@@ -633,7 +633,7 @@
     }
 
     ret = read(pipefd[0], buf, sizeof(buf));
-    EXPECT_EQ(sizeof(buf), ret);
+    EXPECT_EQ(sizeof(buf), (size_t)ret);
     EXPECT_EQ(write_value, buf[0]);
 
     waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */
@@ -672,6 +672,62 @@
     EXPECT_GE(ret, 0);
 }
 
+TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
+    status_t ret;
+    Parcel data, reply;
+
+    ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
+    EXPECT_EQ(NO_ERROR, ret);
+
+    const flat_binder_object *fb = reply.readObject(false);
+    ASSERT_TRUE(fb != NULL);
+    EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type);
+    EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle));
+    EXPECT_EQ((binder_uintptr_t)0, fb->cookie);
+    EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32);
+}
+
+TEST_F(BinderLibTest, FreedBinder) {
+    status_t ret;
+
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != NULL);
+
+    __u32 freedHandle;
+    wp<IBinder> keepFreedBinder;
+    {
+        Parcel data, reply;
+        data.writeBool(false); /* request weak reference */
+        ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
+        ASSERT_EQ(NO_ERROR, ret);
+        struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
+        freedHandle = freed->handle;
+        /* Add a weak ref to the freed binder so the driver does not
+         * delete its reference to it - otherwise the transaction
+         * fails regardless of whether the driver is fixed.
+         */
+        keepFreedBinder = reply.readWeakBinder();
+    }
+    {
+        Parcel data, reply;
+        data.writeStrongBinder(server);
+        /* Replace original handle with handle to the freed binder */
+        struct flat_binder_object *strong = (struct flat_binder_object *)(data.data());
+        __u32 oldHandle = strong->handle;
+        strong->handle = freedHandle;
+        ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply);
+        /* Returns DEAD_OBJECT (-32) if target crashes and
+         * FAILED_TRANSACTION if the driver rejects the invalid
+         * object.
+         */
+        EXPECT_EQ((status_t)FAILED_TRANSACTION, ret);
+        /* Restore original handle so parcel destructor does not use
+         * the wrong handle.
+         */
+        strong->handle = oldHandle;
+    }
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -739,14 +795,10 @@
                 }
                 if (ret > 0) {
                     if (m_serverStartRequested) {
-#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
-                        ret = pthread_cond_timeout_np(&m_serverWaitCond, &m_serverWaitMutex, 5000);
-#else
                         struct timespec ts;
                         clock_gettime(CLOCK_REALTIME, &ts);
                         ts.tv_sec += 5;
                         ret = pthread_cond_timedwait(&m_serverWaitCond, &m_serverWaitMutex, &ts);
-#endif
                     }
                     if (m_serverStartRequested) {
                         m_serverStartRequested = false;
@@ -777,6 +829,9 @@
                 binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_SELF_TRANSACTION:
+                reply->writeStrongBinder(this);
+                return NO_ERROR;
             case BINDER_LIB_TEST_GET_ID_TRANSACTION:
                 reply->writeInt32(m_id);
                 return NO_ERROR;
@@ -890,6 +945,16 @@
                 while (wait(NULL) != -1 || errno != ECHILD)
                     ;
                 exit(EXIT_SUCCESS);
+            case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: {
+                bool strongRef = data.readBool();
+                sp<IBinder> binder = new BBinder();
+                if (strongRef) {
+                    reply->writeStrongBinder(binder);
+                } else {
+                    reply->writeWeakBinder(binder);
+                }
+                return NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             };
@@ -906,6 +971,8 @@
 
 int run_server(int index, int readypipefd)
 {
+    binderLibTestServiceName += String16(binderserversuffix);
+
     status_t ret;
     sp<IServiceManager> sm = defaultServiceManager();
     {
@@ -936,15 +1003,19 @@
 int main(int argc, char **argv) {
     int ret;
 
-    if (argc == 3 && !strcmp(argv[1], "--servername")) {
+    if (argc == 4 && !strcmp(argv[1], "--servername")) {
         binderservername = argv[2];
     } else {
         binderservername = argv[0];
     }
 
-    if (argc == 4 && !strcmp(argv[1], binderserverarg)) {
+    if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
+        binderserversuffix = argv[4];
         return run_server(atoi(argv[2]), atoi(argv[3]));
     }
+    binderserversuffix = new char[16];
+    snprintf(binderserversuffix, 16, "%d", getpid());
+    binderLibTestServiceName += String16(binderserversuffix);
 
     ::testing::InitGoogleTest(&argc, argv);
     binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv());
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
new file mode 100644
index 0000000..6a16e24
--- /dev/null
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -0,0 +1,819 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/SafeInterface.h>
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ProcessState.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <gtest/gtest.h>
+#pragma clang diagnostic pop
+
+#include <utils/LightRefBase.h>
+#include <utils/NativeHandle.h>
+
+#include <cutils/native_handle.h>
+
+#include <optional>
+
+#include <sys/eventfd.h>
+
+using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
+
+namespace android {
+namespace tests {
+
+enum class TestEnum : uint32_t {
+    INVALID = 0,
+    INITIAL = 1,
+    FINAL = 2,
+};
+
+// This class serves two purposes:
+//   1) It ensures that the implementation doesn't require copying or moving the data (for
+//      efficiency purposes)
+//   2) It tests that Parcelables can be passed correctly
+class NoCopyNoMove : public Parcelable {
+public:
+    NoCopyNoMove() = default;
+    explicit NoCopyNoMove(int32_t value) : mValue(value) {}
+    ~NoCopyNoMove() override = default;
+
+    // Not copyable
+    NoCopyNoMove(const NoCopyNoMove&) = delete;
+    NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
+
+    // Not movable
+    NoCopyNoMove(NoCopyNoMove&&) = delete;
+    NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
+
+    // Parcelable interface
+    status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+    status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+    int32_t getValue() const { return mValue; }
+    void setValue(int32_t value) { mValue = value; }
+
+private:
+    int32_t mValue = 0;
+    uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded
+};
+
+struct TestFlattenable : Flattenable<TestFlattenable> {
+    TestFlattenable() = default;
+    explicit TestFlattenable(int32_t v) : value(v) {}
+
+    // Flattenable protocol
+    size_t getFlattenedSize() const { return sizeof(value); }
+    size_t getFdCount() const { return 0; }
+    status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+        FlattenableUtils::write(buffer, size, value);
+        return NO_ERROR;
+    }
+    status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+        FlattenableUtils::read(buffer, size, value);
+        return NO_ERROR;
+    }
+
+    int32_t value = 0;
+};
+
+struct TestLightFlattenable : LightFlattenablePod<TestLightFlattenable> {
+    TestLightFlattenable() = default;
+    explicit TestLightFlattenable(int32_t v) : value(v) {}
+    int32_t value = 0;
+};
+
+// It seems like this should be able to inherit from TestFlattenable (to avoid duplicating code),
+// but the SafeInterface logic can't easily be extended to find an indirect Flattenable<T>
+// base class
+class TestLightRefBaseFlattenable : public Flattenable<TestLightRefBaseFlattenable>,
+                                    public LightRefBase<TestLightRefBaseFlattenable> {
+public:
+    TestLightRefBaseFlattenable() = default;
+    explicit TestLightRefBaseFlattenable(int32_t v) : value(v) {}
+
+    // Flattenable protocol
+    size_t getFlattenedSize() const { return sizeof(value); }
+    size_t getFdCount() const { return 0; }
+    status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+        FlattenableUtils::write(buffer, size, value);
+        return NO_ERROR;
+    }
+    status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+        FlattenableUtils::read(buffer, size, value);
+        return NO_ERROR;
+    }
+
+    int32_t value = 0;
+};
+
+class TestParcelable : public Parcelable {
+public:
+    TestParcelable() = default;
+    explicit TestParcelable(int32_t value) : mValue(value) {}
+    TestParcelable(const TestParcelable& other) : TestParcelable(other.mValue) {}
+    TestParcelable(TestParcelable&& other) : TestParcelable(other.mValue) {}
+
+    // Parcelable interface
+    status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+    status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+    int32_t getValue() const { return mValue; }
+    void setValue(int32_t value) { mValue = value; }
+
+private:
+    int32_t mValue = 0;
+};
+
+class ExitOnDeath : public IBinder::DeathRecipient {
+public:
+    ~ExitOnDeath() override = default;
+
+    void binderDied(const wp<IBinder>& /*who*/) override {
+        ALOG(LOG_INFO, "ExitOnDeath", "Exiting");
+        exit(0);
+    }
+};
+
+// This callback class is used to test both one-way transactions and that sp<IInterface> can be
+// passed correctly
+class ICallback : public IInterface {
+public:
+    DECLARE_META_INTERFACE(Callback)
+
+    enum class Tag : uint32_t {
+        OnCallback = IBinder::FIRST_CALL_TRANSACTION,
+        Last,
+    };
+
+    virtual void onCallback(int32_t aPlusOne) = 0;
+};
+
+class BpCallback : public SafeBpInterface<ICallback> {
+public:
+    explicit BpCallback(const sp<IBinder>& impl) : SafeBpInterface<ICallback>(impl, getLogTag()) {}
+
+    void onCallback(int32_t aPlusOne) override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemoteAsync<decltype(&ICallback::onCallback)>(Tag::OnCallback, aPlusOne);
+    }
+
+private:
+    static constexpr const char* getLogTag() { return "BpCallback"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback");
+#pragma clang diagnostic pop
+
+class BnCallback : public SafeBnInterface<ICallback> {
+public:
+    BnCallback() : SafeBnInterface("BnCallback") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t /*flags*/) override {
+        EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+        EXPECT_LT(code, static_cast<uint32_t>(ICallback::Tag::Last));
+        ICallback::Tag tag = static_cast<ICallback::Tag>(code);
+        switch (tag) {
+            case ICallback::Tag::OnCallback: {
+                return callLocalAsync(data, reply, &ICallback::onCallback);
+            }
+            case ICallback::Tag::Last:
+                // Should not be possible because of the asserts at the beginning of the method
+                [&]() { FAIL(); }();
+                return UNKNOWN_ERROR;
+        }
+    }
+};
+
+class ISafeInterfaceTest : public IInterface {
+public:
+    DECLARE_META_INTERFACE(SafeInterfaceTest)
+
+    enum class Tag : uint32_t {
+        SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+        ReturnsNoMemory,
+        LogicalNot,
+        ModifyEnum,
+        IncrementFlattenable,
+        IncrementLightFlattenable,
+        IncrementLightRefBaseFlattenable,
+        IncrementNativeHandle,
+        IncrementNoCopyNoMove,
+        IncrementParcelableVector,
+        ToUpper,
+        CallMeBack,
+        IncrementInt32,
+        IncrementUint32,
+        IncrementInt64,
+        IncrementUint64,
+        IncrementTwo,
+        Last,
+    };
+
+    // This is primarily so that the remote service dies when the test does, but it also serves to
+    // test the handling of sp<IBinder> and non-const methods
+    virtual status_t setDeathToken(const sp<IBinder>& token) = 0;
+
+    // This is the most basic test since it doesn't require parceling any arguments
+    virtual status_t returnsNoMemory() const = 0;
+
+    // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
+    virtual status_t logicalNot(bool a, bool* notA) const = 0;
+    virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
+    virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
+    virtual status_t increment(const TestLightFlattenable& a,
+                               TestLightFlattenable* aPlusOne) const = 0;
+    virtual status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+                               sp<TestLightRefBaseFlattenable>* aPlusOne) const = 0;
+    virtual status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const = 0;
+    virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0;
+    virtual status_t increment(const std::vector<TestParcelable>& a,
+                               std::vector<TestParcelable>* aPlusOne) const = 0;
+    virtual status_t toUpper(const String8& str, String8* upperStr) const = 0;
+    // As mentioned above, sp<IBinder> is already tested by setDeathToken
+    virtual void callMeBack(const sp<ICallback>& callback, int32_t a) const = 0;
+    virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0;
+    virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0;
+    virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0;
+    virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0;
+
+    // This tests that input/output parameter interleaving works correctly
+    virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b,
+                               int32_t* bPlusOne) const = 0;
+};
+
+class BpSafeInterfaceTest : public SafeBpInterface<ISafeInterfaceTest> {
+public:
+    explicit BpSafeInterfaceTest(const sp<IBinder>& impl)
+          : SafeBpInterface<ISafeInterfaceTest>(impl, getLogTag()) {}
+
+    status_t setDeathToken(const sp<IBinder>& token) override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<decltype(&ISafeInterfaceTest::setDeathToken)>(Tag::SetDeathToken, token);
+    }
+    status_t returnsNoMemory() const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<decltype(&ISafeInterfaceTest::returnsNoMemory)>(Tag::ReturnsNoMemory);
+    }
+    status_t logicalNot(bool a, bool* notA) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+    }
+    status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<decltype(&ISafeInterfaceTest::modifyEnum)>(Tag::ModifyEnum, a, b);
+    }
+    status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+        using Signature =
+                status_t (ISafeInterfaceTest::*)(const TestFlattenable&, TestFlattenable*) const;
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<Signature>(Tag::IncrementFlattenable, a, aPlusOne);
+    }
+    status_t increment(const TestLightFlattenable& a,
+                       TestLightFlattenable* aPlusOne) const override {
+        using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable&,
+                                                           TestLightFlattenable*) const;
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<Signature>(Tag::IncrementLightFlattenable, a, aPlusOne);
+    }
+    status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+                       sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+        using Signature = status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+                                                           sp<TestLightRefBaseFlattenable>*) const;
+        return callRemote<Signature>(Tag::IncrementLightRefBaseFlattenable, a, aPlusOne);
+    }
+    status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature =
+                status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&, sp<NativeHandle>*) const;
+        return callRemote<Signature>(Tag::IncrementNativeHandle, a, aPlusOne);
+    }
+    status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+                                                           NoCopyNoMove* aPlusOne) const;
+        return callRemote<Signature>(Tag::IncrementNoCopyNoMove, a, aPlusOne);
+    }
+    status_t increment(const std::vector<TestParcelable>& a,
+                       std::vector<TestParcelable>* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+                                                           std::vector<TestParcelable>*);
+        return callRemote<Signature>(Tag::IncrementParcelableVector, a, aPlusOne);
+    }
+    status_t toUpper(const String8& str, String8* upperStr) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemote<decltype(&ISafeInterfaceTest::toUpper)>(Tag::ToUpper, str, upperStr);
+    }
+    void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return callRemoteAsync<decltype(&ISafeInterfaceTest::callMeBack)>(Tag::CallMeBack, callback,
+                                                                          a);
+    }
+    status_t increment(int32_t a, int32_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+        return callRemote<Signature>(Tag::IncrementInt32, a, aPlusOne);
+    }
+    status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+        return callRemote<Signature>(Tag::IncrementUint32, a, aPlusOne);
+    }
+    status_t increment(int64_t a, int64_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+        return callRemote<Signature>(Tag::IncrementInt64, a, aPlusOne);
+    }
+    status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+        return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne);
+    }
+    status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature =
+                status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const;
+        return callRemote<Signature>(Tag::IncrementTwo, a, aPlusOne, b, bPlusOne);
+    }
+
+private:
+    static constexpr const char* getLogTag() { return "BpSafeInterfaceTest"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest");
+
+static sp<IBinder::DeathRecipient> getDeathRecipient() {
+    static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath;
+    return recipient;
+}
+#pragma clang diagnostic pop
+
+class BnSafeInterfaceTest : public SafeBnInterface<ISafeInterfaceTest> {
+public:
+    BnSafeInterfaceTest() : SafeBnInterface(getLogTag()) {}
+
+    status_t setDeathToken(const sp<IBinder>& token) override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        token->linkToDeath(getDeathRecipient());
+        return NO_ERROR;
+    }
+    status_t returnsNoMemory() const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        return NO_MEMORY;
+    }
+    status_t logicalNot(bool a, bool* notA) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *notA = !a;
+        return NO_ERROR;
+    }
+    status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
+        return NO_ERROR;
+    }
+    status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        aPlusOne->value = a.value + 1;
+        return NO_ERROR;
+    }
+    status_t increment(const TestLightFlattenable& a,
+                       TestLightFlattenable* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        aPlusOne->value = a.value + 1;
+        return NO_ERROR;
+    }
+    status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+                       sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = new TestLightRefBaseFlattenable(a->value + 1);
+        return NO_ERROR;
+    }
+    status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        native_handle* rawHandle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+        if (rawHandle == nullptr) return NO_MEMORY;
+
+        // Copy the fd over directly
+        rawHandle->data[0] = dup(a->handle()->data[0]);
+
+        // Increment the int
+        rawHandle->data[1] = a->handle()->data[1] + 1;
+
+        // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+        // the native_handle when it goes out of scope
+        *aPlusOne = NativeHandle::create(rawHandle, true);
+        return NO_ERROR;
+    }
+    status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        aPlusOne->setValue(a.getValue() + 1);
+        return NO_ERROR;
+    }
+    status_t increment(const std::vector<TestParcelable>& a,
+                       std::vector<TestParcelable>* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        aPlusOne->resize(a.size());
+        for (size_t i = 0; i < a.size(); ++i) {
+            (*aPlusOne)[i].setValue(a[i].getValue() + 1);
+        }
+        return NO_ERROR;
+    }
+    status_t toUpper(const String8& str, String8* upperStr) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *upperStr = str;
+        upperStr->toUpper();
+        return NO_ERROR;
+    }
+    void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        callback->onCallback(a + 1);
+    }
+    status_t increment(int32_t a, int32_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = a + 1;
+        return NO_ERROR;
+    }
+    status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = a + 1;
+        return NO_ERROR;
+    }
+    status_t increment(int64_t a, int64_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = a + 1;
+        return NO_ERROR;
+    }
+    status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = a + 1;
+        return NO_ERROR;
+    }
+    status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        *aPlusOne = a + 1;
+        *bPlusOne = b + 1;
+        return NO_ERROR;
+    }
+
+    // BnInterface
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t /*flags*/) override {
+        EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+        EXPECT_LT(code, static_cast<uint32_t>(Tag::Last));
+        ISafeInterfaceTest::Tag tag = static_cast<ISafeInterfaceTest::Tag>(code);
+        switch (tag) {
+            case ISafeInterfaceTest::Tag::SetDeathToken: {
+                return callLocal(data, reply, &ISafeInterfaceTest::setDeathToken);
+            }
+            case ISafeInterfaceTest::Tag::ReturnsNoMemory: {
+                return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
+            }
+            case ISafeInterfaceTest::Tag::LogicalNot: {
+                return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+            }
+            case ISafeInterfaceTest::Tag::ModifyEnum: {
+                return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
+            }
+            case ISafeInterfaceTest::Tag::IncrementFlattenable: {
+                using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable& a,
+                                                                   TestFlattenable* aPlusOne) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementLightFlattenable: {
+                using Signature =
+                        status_t (ISafeInterfaceTest::*)(const TestLightFlattenable& a,
+                                                         TestLightFlattenable* aPlusOne) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementLightRefBaseFlattenable: {
+                using Signature =
+                        status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+                                                         sp<TestLightRefBaseFlattenable>*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementNativeHandle: {
+                using Signature = status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&,
+                                                                   sp<NativeHandle>*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementNoCopyNoMove: {
+                using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+                                                                   NoCopyNoMove* aPlusOne) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementParcelableVector: {
+                using Signature =
+                        status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+                                                         std::vector<TestParcelable>*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::ToUpper: {
+                return callLocal(data, reply, &ISafeInterfaceTest::toUpper);
+            }
+            case ISafeInterfaceTest::Tag::CallMeBack: {
+                return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack);
+            }
+            case ISafeInterfaceTest::Tag::IncrementInt32: {
+                using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementUint32: {
+                using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementInt64: {
+                using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementUint64: {
+                using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::IncrementTwo: {
+                using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t,
+                                                                   int32_t*) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+            }
+            case ISafeInterfaceTest::Tag::Last:
+                // Should not be possible because of the asserts at the beginning of the method
+                [&]() { FAIL(); }();
+                return UNKNOWN_ERROR;
+        }
+    }
+
+private:
+    static constexpr const char* getLogTag() { return "BnSafeInterfaceTest"; }
+};
+
+class SafeInterfaceTest : public ::testing::Test {
+public:
+    SafeInterfaceTest() : mSafeInterfaceTest(getRemoteService()) {
+        ProcessState::self()->startThreadPool();
+    }
+    ~SafeInterfaceTest() override = default;
+
+protected:
+    sp<ISafeInterfaceTest> mSafeInterfaceTest;
+
+private:
+    static constexpr const char* getLogTag() { return "SafeInterfaceTest"; }
+
+    sp<ISafeInterfaceTest> getRemoteService() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+        static std::mutex sMutex;
+        static sp<ISafeInterfaceTest> sService;
+        static sp<IBinder> sDeathToken = new BBinder;
+#pragma clang diagnostic pop
+
+        std::unique_lock<decltype(sMutex)> lock;
+        if (sService == nullptr) {
+            ALOG(LOG_INFO, getLogTag(), "Forking remote process");
+            pid_t forkPid = fork();
+            EXPECT_NE(forkPid, -1);
+
+            const String16 serviceName("SafeInterfaceTest");
+
+            if (forkPid == 0) {
+                ALOG(LOG_INFO, getLogTag(), "Remote process checking in");
+                sp<ISafeInterfaceTest> nativeService = new BnSafeInterfaceTest;
+                defaultServiceManager()->addService(serviceName,
+                                                    IInterface::asBinder(nativeService));
+                ProcessState::self()->startThreadPool();
+                IPCThreadState::self()->joinThreadPool();
+                // We shouldn't get to this point
+                [&]() { FAIL(); }();
+            }
+
+            sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+            sService = interface_cast<ISafeInterfaceTest>(binder);
+            EXPECT_TRUE(sService != nullptr);
+
+            sService->setDeathToken(sDeathToken);
+        }
+
+        return sService;
+    }
+};
+
+TEST_F(SafeInterfaceTest, TestReturnsNoMemory) {
+    status_t result = mSafeInterfaceTest->returnsNoMemory();
+    ASSERT_EQ(NO_MEMORY, result);
+}
+
+TEST_F(SafeInterfaceTest, TestLogicalNot) {
+    const bool a = true;
+    bool notA = true;
+    status_t result = mSafeInterfaceTest->logicalNot(a, &notA);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(!a, notA);
+    // Test both since we don't want to accidentally catch a default false somewhere
+    const bool b = false;
+    bool notB = false;
+    result = mSafeInterfaceTest->logicalNot(b, &notB);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(!b, notB);
+}
+
+TEST_F(SafeInterfaceTest, TestModifyEnum) {
+    const TestEnum a = TestEnum::INITIAL;
+    TestEnum b = TestEnum::INVALID;
+    status_t result = mSafeInterfaceTest->modifyEnum(a, &b);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(TestEnum::FINAL, b);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementFlattenable) {
+    const TestFlattenable a{1};
+    TestFlattenable aPlusOne{0};
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightFlattenable) {
+    const TestLightFlattenable a{1};
+    TestLightFlattenable aPlusOne{0};
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightRefBaseFlattenable) {
+    sp<TestLightRefBaseFlattenable> a = new TestLightRefBaseFlattenable{1};
+    sp<TestLightRefBaseFlattenable> aPlusOne;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_NE(nullptr, aPlusOne.get());
+    ASSERT_EQ(a->value + 1, aPlusOne->value);
+}
+
+namespace { // Anonymous namespace
+
+bool fdsAreEquivalent(int a, int b) {
+    struct stat statA {};
+    struct stat statB {};
+    if (fstat(a, &statA) != 0) return false;
+    if (fstat(b, &statB) != 0) return false;
+    return (statA.st_dev == statB.st_dev) && (statA.st_ino == statB.st_ino);
+}
+
+} // Anonymous namespace
+
+TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) {
+    // Create an fd we can use to send and receive from the remote process
+    base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)};
+    ASSERT_NE(-1, eventFd);
+
+    // Determine the maximum number of fds this process can have open
+    struct rlimit limit {};
+    ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit));
+    uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur);
+
+    // Perform this test enough times to rule out fd leaks
+    for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) {
+        native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+        ASSERT_NE(nullptr, handle);
+        handle->data[0] = dup(eventFd.get());
+        handle->data[1] = 1;
+
+        // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+        // the native_handle when it goes out of scope
+        sp<NativeHandle> a = NativeHandle::create(handle, true);
+
+        sp<NativeHandle> aPlusOne;
+        status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+        ASSERT_EQ(NO_ERROR, result);
+        ASSERT_TRUE(fdsAreEquivalent(a->handle()->data[0], aPlusOne->handle()->data[0]));
+        ASSERT_EQ(a->handle()->data[1] + 1, aPlusOne->handle()->data[1]);
+    }
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementNoCopyNoMove) {
+    const NoCopyNoMove a{1};
+    NoCopyNoMove aPlusOne{0};
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a.getValue() + 1, aPlusOne.getValue());
+}
+
+TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) {
+    const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}};
+    std::vector<TestParcelable> aPlusOne;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(a.size(), aPlusOne.size());
+    for (size_t i = 0; i < a.size(); ++i) {
+        ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue());
+    }
+}
+
+TEST_F(SafeInterfaceTest, TestToUpper) {
+    const String8 str{"Hello, world!"};
+    String8 upperStr;
+    status_t result = mSafeInterfaceTest->toUpper(str, &upperStr);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"});
+}
+
+TEST_F(SafeInterfaceTest, TestCallMeBack) {
+    class CallbackReceiver : public BnCallback {
+    public:
+        void onCallback(int32_t aPlusOne) override {
+            ALOG(LOG_INFO, "CallbackReceiver", "%s", __PRETTY_FUNCTION__);
+            std::unique_lock<decltype(mMutex)> lock(mMutex);
+            mValue = aPlusOne;
+            mCondition.notify_one();
+        }
+
+        std::optional<int32_t> waitForCallback() {
+            std::unique_lock<decltype(mMutex)> lock(mMutex);
+            bool success =
+                    mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); });
+            return success ? mValue : std::nullopt;
+        }
+
+    private:
+        std::mutex mMutex;
+        std::condition_variable mCondition;
+        std::optional<int32_t> mValue;
+    };
+
+    sp<CallbackReceiver> receiver = new CallbackReceiver;
+    const int32_t a = 1;
+    mSafeInterfaceTest->callMeBack(receiver, a);
+    auto result = receiver->waitForCallback();
+    ASSERT_TRUE(result);
+    ASSERT_EQ(a + 1, *result);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt32) {
+    const int32_t a = 1;
+    int32_t aPlusOne = 0;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint32) {
+    const uint32_t a = 1;
+    uint32_t aPlusOne = 0;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt64) {
+    const int64_t a = 1;
+    int64_t aPlusOne = 0;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint64) {
+    const uint64_t a = 1;
+    uint64_t aPlusOne = 0;
+    status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementTwo) {
+    const int32_t a = 1;
+    int32_t aPlusOne = 0;
+    const int32_t b = 2;
+    int32_t bPlusOne = 0;
+    status_t result = mSafeInterfaceTest->increment(1, &aPlusOne, 2, &bPlusOne);
+    ASSERT_EQ(NO_ERROR, result);
+    ASSERT_EQ(a + 1, aPlusOne);
+    ASSERT_EQ(b + 1, bPlusOne);
+}
+
+} // namespace tests
+} // namespace android
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
new file mode 100644
index 0000000..f6dd22d
--- /dev/null
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include <binder/Debug.h>
+
+static void CheckMessage(const CapturedStderr& cap,
+                         const char* expected,
+                         bool singleline) {
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    if (singleline)
+        output.erase(std::remove(output.begin(), output.end(), '\n'));
+    ASSERT_STREQ(output.c_str(), expected);
+}
+
+#define CHECK_LOG_(input, expect, singleline)    \
+{                                                \
+    CapturedStderr cap;                          \
+    android::aerr << input << android::endl;     \
+    CheckMessage(cap, expect, singleline);       \
+}                                                \
+
+#define CHECK_VAL_(val, singleline)              \
+{                                                \
+    std::stringstream ss;                        \
+    ss << val;                                   \
+    std::string s = ss.str();                    \
+    CHECK_LOG_(val, s.c_str(), singleline);      \
+}                                                \
+
+#define CHECK_LOG(input, expect) CHECK_LOG_(input, expect, true)
+#define CHECK_VAL(val) CHECK_VAL_(val, true)
+
+TEST(TextOutput, HandlesStdEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << std::endl;
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesCEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << "\n";
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesAndroidEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << android::endl;
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandleEmptyString) {
+    CHECK_LOG("", "");
+}
+
+TEST(TextOutput, HandleString) {
+    CHECK_LOG("foobar", "foobar");
+}
+
+TEST(TextOutput, HandleNum) {
+    CHECK_LOG(12345, "12345");
+}
+
+TEST(TextOutput, HandleBool) {
+    CHECK_LOG(false, "false");
+}
+
+TEST(TextOutput, HandleChar) {
+    CHECK_LOG('T', "T");
+}
+
+TEST(TextOutput, HandleParcel) {
+    android::Parcel val;
+    CHECK_LOG(val, "Parcel(NULL)");
+}
+
+TEST(TextOutput, HandleHexDump) {
+    const char buf[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+    android::HexDump val(buf, sizeof(buf));
+    CHECK_LOG(val, "03020100 07060504 0b0a0908 0f0e0d0c '................'");
+}
+
+TEST(TextOutput, HandleHexDumpCustom) {
+    const char buf[4] = {0x11,0x22,0x33,0x44};
+    android::HexDump val(buf, sizeof(buf), 4);
+    CHECK_LOG(val, "11 22 33 44 '.\"3D'");
+}
+
+TEST(TextOutput, HandleTypeCode) {
+    android::TypeCode val(1234);
+    CHECK_LOG(val, "'\\x04\\xd2'");
+}
+
+TEST(TextOutput, HandleCookie) {
+    int32_t val = 321; //0x141
+    CHECK_LOG((void*)(long)val, "0x141");
+}
+
+TEST(TextOutput, HandleString8) {
+    android::String8 val("foobar");
+    CHECK_LOG(val, "foobar");
+}
+
+TEST(TextOutput, HandleString16) {
+    android::String16 val("foobar");
+    CHECK_LOG(val, "foobar");
+}
+
+template <typename T>
+class TextTest : public testing::Test {};
+
+typedef testing::Types<short, unsigned short,
+                       int, unsigned int,
+                       long, unsigned long,
+                       long long, unsigned long long,
+                       float, double, long double> TestTypes;
+TYPED_TEST_CASE(TextTest, TestTypes);
+
+TYPED_TEST(TextTest, TextMax)
+{
+    TypeParam max = std::numeric_limits<TypeParam>::max();
+    CHECK_VAL(max);
+}
+
+TYPED_TEST(TextTest, TestMin)
+{
+    TypeParam min = std::numeric_limits<TypeParam>::min();
+    CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestDenom)
+{
+    TypeParam min = std::numeric_limits<TypeParam>::denorm_min();
+    CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestEpsilon)
+{
+    TypeParam eps = std::numeric_limits<TypeParam>::epsilon();
+    CHECK_VAL(eps);
+}
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 71b96d4..6e8f7df 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -170,6 +170,8 @@
     int num,
     int worker_count,
     int iterations,
+    int payload_size,
+    bool cs_pair,
     Pipe p)
 {
     // Create BinderWorkerService and for go.
@@ -182,22 +184,32 @@
     p.signal();
     p.wait();
 
+    // If client/server pairs, then half the workers are
+    // servers and half are clients
+    int server_count = cs_pair ? worker_count / 2 : worker_count;
+
     // Get references to other binder services.
     cout << "Created BinderWorker" << num << endl;
     (void)worker_count;
     vector<sp<IBinder> > workers;
-    for (int i = 0; i < worker_count; i++) {
+    for (int i = 0; i < server_count; i++) {
         if (num == i)
             continue;
         workers.push_back(serviceMgr->getService(generateServiceName(i)));
     }
 
-    // Run the benchmark.
+    // Run the benchmark if client
     ProcResults results;
     chrono::time_point<chrono::high_resolution_clock> start, end;
-    for (int i = 0; i < iterations; i++) {
-        int target = rand() % workers.size();
+    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;
+
+	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();
@@ -210,6 +222,7 @@
            exit(EXIT_FAILURE);
         }
     }
+
     // Signal completion to master and wait.
     p.signal();
     p.wait();
@@ -221,7 +234,7 @@
     exit(EXIT_SUCCESS);
 }
 
-Pipe make_worker(int num, int iterations, int worker_count)
+Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair)
 {
     auto pipe_pair = Pipe::createPipePair();
     pid_t pid = fork();
@@ -230,7 +243,7 @@
         return move(get<0>(pipe_pair));
     } else {
         /* child */
-        worker_fx(num, worker_count, iterations, move(get<1>(pipe_pair)));
+        worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair)));
         /* never get here */
         return move(get<0>(pipe_pair));
     }
@@ -255,6 +268,8 @@
 {
     int workers = 2;
     int iterations = 10000;
+    int payload_size = 0;
+    bool cs_pair = false;
     (void)argc;
     (void)argv;
     vector<Pipe> pipes;
@@ -271,11 +286,21 @@
             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));
+        pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
     }
     wait_all(pipes);
 
diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp
new file mode 100644
index 0000000..c8f4697
--- /dev/null
+++ b/libs/binder/tests/binderValueTypeTest.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/Value.h>
+#include <binder/Debug.h>
+
+using ::android::binder::Value;
+using ::android::os::PersistableBundle;
+using ::android::String16;
+using ::std::vector;
+
+#define VALUE_TYPE_TEST(T, TYPENAME, VAL)         \
+    TEST(ValueType, Handles ## TYPENAME) {        \
+        T x = VAL;                                \
+        T y = T();                                \
+        Value value = VAL;                        \
+        ASSERT_FALSE(value.empty());              \
+        ASSERT_TRUE(value.is ## TYPENAME ());     \
+        ASSERT_TRUE(value.get ## TYPENAME (&y));  \
+        ASSERT_EQ(x, y);                          \
+        ASSERT_EQ(value, Value(y));               \
+        value.put ## TYPENAME (x);                \
+        ASSERT_EQ(value, Value(y));               \
+        value = Value();                          \
+        ASSERT_TRUE(value.empty());               \
+        ASSERT_NE(value, Value(y));               \
+        value = y;                                \
+        ASSERT_EQ(value, Value(x));               \
+    }
+
+#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL)      \
+    TEST(ValueType, Handles ## TYPENAME ## Vector) {  \
+        vector<T> x;                                  \
+        vector<T> y;                                  \
+        x.push_back(VAL);                             \
+        x.push_back(T());                             \
+        Value value(x);                               \
+        ASSERT_FALSE(value.empty());                  \
+        ASSERT_TRUE(value.is ## TYPENAME ## Vector());    \
+        ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \
+        ASSERT_EQ(x, y);                              \
+        ASSERT_EQ(value, Value(y));                   \
+        value.put ## TYPENAME ## Vector(x);           \
+        ASSERT_EQ(value, Value(y));                   \
+        value = Value();                              \
+        ASSERT_TRUE(value.empty());                   \
+        ASSERT_NE(value, Value(y));                   \
+        value = y;                                    \
+        ASSERT_EQ(value, Value(x));                   \
+    }
+
+VALUE_TYPE_TEST(bool, Boolean, true)
+VALUE_TYPE_TEST(int32_t, Int, 31337)
+VALUE_TYPE_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_VECTOR_TEST(bool, Boolean, true)
+VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337)
+VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle())
+
+TEST(ValueType, HandlesClear) {
+    Value value;
+    ASSERT_TRUE(value.empty());
+    value.putInt(31337);
+    ASSERT_FALSE(value.empty());
+    value.clear();
+    ASSERT_TRUE(value.empty());
+}
+
+TEST(ValueType, HandlesSwap) {
+    Value value_a, value_b;
+    int32_t int_x;
+    value_a.putInt(31337);
+    ASSERT_FALSE(value_a.empty());
+    ASSERT_TRUE(value_b.empty());
+    value_a.swap(value_b);
+    ASSERT_FALSE(value_b.empty());
+    ASSERT_TRUE(value_a.empty());
+    ASSERT_TRUE(value_b.getInt(&int_x));
+    ASSERT_EQ(31337, int_x);
+}
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
new file mode 100644
index 0000000..13f03b1
--- /dev/null
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -0,0 +1,502 @@
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include <iomanip>
+#include <iostream>
+#include <tuple>
+#include <vector>
+
+#include <pthread.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fstream>
+
+using namespace std;
+using namespace android;
+
+enum BinderWorkerServiceCode {
+  BINDER_NOP = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+#define ASSERT(cond)                                                \
+  do {                                                              \
+    if (!(cond)) {                                                  \
+      cerr << __func__ << ":" << __LINE__ << " condition:" << #cond \
+           << " failed\n"                                           \
+           << endl;                                                 \
+      exit(EXIT_FAILURE);                                           \
+    }                                                               \
+  } while (0)
+
+vector<sp<IBinder> > workers;
+
+// the ratio that the service is synced on the same cpu beyond
+// GOOD_SYNC_MIN is considered as good
+#define GOOD_SYNC_MIN (0.6)
+
+#define DUMP_PRESICION 2
+
+string trace_path = "/sys/kernel/debug/tracing";
+
+// the default value
+int no_process = 2;
+int iterations = 100;
+int payload_size = 16;
+int no_inherent = 0;
+int no_sync = 0;
+int verbose = 0;
+int trace;
+
+bool traceIsOn() {
+  fstream file;
+  file.open(trace_path + "/tracing_on", ios::in);
+  char on;
+  file >> on;
+  file.close();
+  return on == '1';
+}
+
+void traceStop() {
+  ofstream file;
+  file.open(trace_path + "/tracing_on", ios::out | ios::trunc);
+  file << '0' << endl;
+  file.close();
+}
+
+// the deadline latency that we are interested in
+uint64_t deadline_us = 2500;
+
+int thread_pri() {
+  struct sched_param param;
+  int policy;
+  ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
+  return param.sched_priority;
+}
+
+void thread_dump(const char* prefix) {
+  struct sched_param param;
+  int policy;
+  if (!verbose) return;
+  cout << "--------------------------------------------------" << endl;
+  cout << setw(12) << left << prefix << " pid: " << getpid()
+       << " tid: " << gettid() << " cpu: " << sched_getcpu() << endl;
+  ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
+  string s = (policy == SCHED_OTHER)
+                 ? "SCHED_OTHER"
+                 : (policy == SCHED_FIFO)
+                       ? "SCHED_FIFO"
+                       : (policy == SCHED_RR) ? "SCHED_RR" : "???";
+  cout << setw(12) << left << s << param.sched_priority << endl;
+  return;
+}
+
+class BinderWorkerService : public BBinder {
+ public:
+  BinderWorkerService() {
+  }
+  ~BinderWorkerService() {
+  }
+  virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                              uint32_t flags = 0) {
+    (void)flags;
+    (void)data;
+    (void)reply;
+    switch (code) {
+      // The transaction format is like
+      //
+      // data[in]:  int32: caller priority
+      //            int32: caller cpu
+      //
+      // reply[out]: int32: 1 if caller's priority != callee's priority
+      //             int32: 1 if caller's cpu != callee's cpu
+      //
+      // note the caller cpu read here is not always correct
+      // there're still chances that the caller got switched out
+      // right after it read the cpu number and still before the transaction.
+      case BINDER_NOP: {
+        thread_dump("binder");
+        int priority = thread_pri();
+        int priority_caller = data.readInt32();
+        int h = 0, s = 0;
+        if (priority_caller != priority) {
+          h++;
+          if (verbose) {
+            cout << "err priority_caller:" << priority_caller
+                 << ", priority:" << priority << endl;
+          }
+        }
+        if (priority == sched_get_priority_max(SCHED_FIFO)) {
+          int cpu = sched_getcpu();
+          int cpu_caller = data.readInt32();
+          if (cpu != cpu_caller) {
+            s++;
+          }
+        }
+        reply->writeInt32(h);
+        reply->writeInt32(s);
+        return NO_ERROR;
+      }
+      default:
+        return UNKNOWN_TRANSACTION;
+    };
+  }
+};
+
+class Pipe {
+  int m_readFd;
+  int m_writeFd;
+  Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} {
+  }
+  Pipe(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&&) = delete;
+
+ public:
+  Pipe(Pipe&& rval) noexcept {
+    m_readFd = rval.m_readFd;
+    m_writeFd = rval.m_writeFd;
+    rval.m_readFd = 0;
+    rval.m_writeFd = 0;
+  }
+  ~Pipe() {
+    if (m_readFd) close(m_readFd);
+    if (m_writeFd) close(m_writeFd);
+  }
+  void signal() {
+    bool val = true;
+    int error = write(m_writeFd, &val, sizeof(val));
+    ASSERT(error >= 0);
+  };
+  void wait() {
+    bool val = false;
+    int error = read(m_readFd, &val, sizeof(val));
+    ASSERT(error >= 0);
+  }
+  template <typename T>
+  void send(const T& v) {
+    int error = write(m_writeFd, &v, sizeof(T));
+    ASSERT(error >= 0);
+  }
+  template <typename T>
+  void recv(T& v) {
+    int error = read(m_readFd, &v, sizeof(T));
+    ASSERT(error >= 0);
+  }
+  static tuple<Pipe, Pipe> createPipePair() {
+    int a[2];
+    int b[2];
+
+    int error1 = pipe(a);
+    int error2 = pipe(b);
+    ASSERT(error1 >= 0);
+    ASSERT(error2 >= 0);
+
+    return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
+  }
+};
+
+typedef chrono::time_point<chrono::high_resolution_clock> Tick;
+
+static inline Tick tickNow() {
+  return chrono::high_resolution_clock::now();
+}
+
+static inline uint64_t tickNano(Tick& sta, Tick& end) {
+  return uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - sta).count());
+}
+
+struct Results {
+  uint64_t m_best = 0xffffffffffffffffULL;
+  uint64_t m_worst = 0;
+  uint64_t m_transactions = 0;
+  uint64_t m_total_time = 0;
+  uint64_t m_miss = 0;
+  bool tracing;
+  Results(bool _tracing) : tracing(_tracing) {
+  }
+  inline bool miss_deadline(uint64_t nano) {
+    return nano > deadline_us * 1000;
+  }
+  void add_time(uint64_t nano) {
+    m_best = min(nano, m_best);
+    m_worst = max(nano, m_worst);
+    m_transactions += 1;
+    m_total_time += nano;
+    if (miss_deadline(nano)) m_miss++;
+    if (miss_deadline(nano) && tracing) {
+      // There might be multiple process pair running the test concurrently
+      // each may execute following statements and only the first one actually
+      // stop the trace and any traceStop() afterthen has no effect.
+      traceStop();
+      cout << endl;
+      cout << "deadline triggered: halt & stop trace" << endl;
+      cout << "log:" + trace_path + "/trace" << endl;
+      cout << endl;
+      exit(1);
+    }
+  }
+  void dump() {
+    double best = (double)m_best / 1.0E6;
+    double worst = (double)m_worst / 1.0E6;
+    double average = (double)m_total_time / m_transactions / 1.0E6;
+    // FIXME: libjson?
+    int W = DUMP_PRESICION + 2;
+    cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
+         << average << ",\"wst\":" << setw(W) << left << worst
+         << ",\"bst\":" << setw(W) << left << best << ",\"miss\":" << left
+         << m_miss << ",\"meetR\":" << left << setprecision(DUMP_PRESICION + 3)
+         << (1.0 - (double)m_miss / m_transactions) << "}";
+  }
+};
+
+String16 generateServiceName(int num) {
+  char num_str[32];
+  snprintf(num_str, sizeof(num_str), "%d", num);
+  String16 serviceName = String16("binderWorker") + String16(num_str);
+  return serviceName;
+}
+
+static void parcel_fill(Parcel& data, int sz, int priority, int cpu) {
+  ASSERT(sz >= (int)sizeof(uint32_t) * 2);
+  data.writeInt32(priority);
+  data.writeInt32(cpu);
+  sz -= sizeof(uint32_t);
+  while (sz > (int)sizeof(uint32_t)) {
+    data.writeInt32(0);
+    sz -= sizeof(uint32_t);
+  }
+}
+
+typedef struct {
+  void* result;
+  int target;
+} thread_priv_t;
+
+static void* thread_start(void* p) {
+  thread_priv_t* priv = (thread_priv_t*)p;
+  int target = priv->target;
+  Results* results_fifo = (Results*)priv->result;
+  Parcel data, reply;
+  Tick sta, end;
+
+  parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+  thread_dump("fifo-caller");
+
+  sta = tickNow();
+  status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
+  end = tickNow();
+  results_fifo->add_time(tickNano(sta, end));
+
+  no_inherent += reply.readInt32();
+  no_sync += reply.readInt32();
+  return 0;
+}
+
+// create a fifo thread to transact and wait it to finished
+static void thread_transaction(int target, Results* results_fifo) {
+  thread_priv_t thread_priv;
+  void* dummy;
+  pthread_t thread;
+  pthread_attr_t attr;
+  struct sched_param param;
+  thread_priv.target = target;
+  thread_priv.result = results_fifo;
+  ASSERT(!pthread_attr_init(&attr));
+  ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
+  param.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  ASSERT(!pthread_attr_setschedparam(&attr, &param));
+  ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv));
+  ASSERT(!pthread_join(thread, &dummy));
+}
+
+#define is_client(_num) ((_num) >= (no_process / 2))
+
+void worker_fx(int num, int no_process, int iterations, int payload_size,
+               Pipe p) {
+  int dummy;
+  Results results_other(false), results_fifo(trace);
+
+  // Create BinderWorkerService and for go.
+  ProcessState::self()->startThreadPool();
+  sp<IServiceManager> serviceMgr = defaultServiceManager();
+  sp<BinderWorkerService> service = new BinderWorkerService;
+  serviceMgr->addService(generateServiceName(num), service);
+  // init done
+  p.signal();
+  // wait for kick-off
+  p.wait();
+
+  // If client/server pairs, then half the workers are
+  // servers and half are clients
+  int server_count = no_process / 2;
+
+  for (int i = 0; i < server_count; i++) {
+    // self service is in-process so just skip
+    if (num == i) continue;
+    workers.push_back(serviceMgr->getService(generateServiceName(i)));
+  }
+
+  // Client for each pair iterates here
+  // each iterations contains exatcly 2 transactions
+  for (int i = 0; is_client(num) && i < iterations; i++) {
+    Parcel data, reply;
+    Tick sta, end;
+    // the target is paired to make it easier to diagnose
+    int target = num % server_count;
+
+    // 1. transaction by fifo thread
+    thread_transaction(target, &results_fifo);
+    parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+    thread_dump("other-caller");
+
+    // 2. transaction by other thread
+    sta = tickNow();
+    ASSERT(NO_ERROR == workers[target]->transact(BINDER_NOP, data, &reply));
+    end = tickNow();
+    results_other.add_time(tickNano(sta, end));
+
+    no_inherent += reply.readInt32();
+    no_sync += reply.readInt32();
+  }
+  // Signal completion to master and wait.
+  p.signal();
+  p.wait();
+
+  p.send(&dummy);
+  // wait for kill
+  p.wait();
+  // Client for each pair dump here
+  if (is_client(num)) {
+    int no_trans = iterations * 2;
+    double sync_ratio = (1.0 - (double)no_sync / no_trans);
+    // FIXME: libjson?
+    cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
+         << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
+         << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
+         << "\"R\":" << sync_ratio << "," << endl;
+
+    cout << "  \"other_ms\":";
+    results_other.dump();
+    cout << "," << endl;
+    cout << "  \"fifo_ms\": ";
+    results_fifo.dump();
+    cout << endl;
+    cout << "}," << endl;
+  }
+  exit(no_inherent);
+}
+
+Pipe make_process(int num, int iterations, int no_process, int payload_size) {
+  auto pipe_pair = Pipe::createPipePair();
+  pid_t pid = fork();
+  if (pid) {
+    // parent
+    return move(get<0>(pipe_pair));
+  } else {
+    // child
+    thread_dump(is_client(num) ? "client" : "server");
+    worker_fx(num, no_process, iterations, payload_size,
+              move(get<1>(pipe_pair)));
+    // never get here
+    return move(get<0>(pipe_pair));
+  }
+}
+
+void wait_all(vector<Pipe>& v) {
+  for (size_t i = 0; i < v.size(); i++) {
+    v[i].wait();
+  }
+}
+
+void signal_all(vector<Pipe>& v) {
+  for (size_t i = 0; i < v.size(); i++) {
+    v[i].signal();
+  }
+}
+
+// This test is modified from binderThroughputTest.cpp
+int main(int argc, char** argv) {
+  for (int i = 1; i < argc; i++) {
+    if (string(argv[i]) == "-i") {
+      iterations = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-pair") {
+      no_process = 2 * atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-deadline_us") {
+      deadline_us = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-v") {
+      verbose = 1;
+    }
+    // The -trace argument is used like that:
+    //
+    // First start trace with atrace command as usual
+    // >atrace --async_start sched freq
+    //
+    // then use schd-dbg with -trace arguments
+    //./schd-dbg -trace -deadline_us 2500
+    //
+    // This makes schd-dbg to stop trace once it detects a transaction
+    // duration over the deadline. By writing '0' to
+    // /sys/kernel/debug/tracing and halt the process. The tracelog is
+    // then available on /sys/kernel/debug/trace
+    if (string(argv[i]) == "-trace") {
+      trace = 1;
+    }
+  }
+  if (trace && !traceIsOn()) {
+    cout << "trace is not running" << endl;
+    cout << "check " << trace_path + "/tracing_on" << endl;
+    cout << "use atrace --async_start first" << endl;
+    exit(-1);
+  }
+  vector<Pipe> pipes;
+  thread_dump("main");
+  // FIXME: libjson?
+  cout << "{" << endl;
+  cout << "\"cfg\":{\"pair\":" << (no_process / 2)
+       << ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
+       << "}," << endl;
+
+  // the main process fork 2 processes for each pairs
+  // 1 server + 1 client
+  // each has a pipe to communicate with
+  for (int i = 0; i < no_process; i++) {
+    pipes.push_back(make_process(i, iterations, no_process, payload_size));
+  }
+  // wait for init done
+  wait_all(pipes);
+  // kick-off iterations
+  signal_all(pipes);
+  // wait for completion
+  wait_all(pipes);
+  // start to send result
+  signal_all(pipes);
+  for (int i = 0; i < no_process; i++) {
+    int status;
+    // kill
+    pipes[i].signal();
+    wait(&status);
+    // the exit status is number of transactions without priority inheritance
+    // detected in the child process
+    no_inherent += status;
+  }
+  // FIXME: libjson?
+  cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
+       << endl;
+  cout << "}" << endl;
+  return -no_inherent;
+}
diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp
new file mode 100644
index 0000000..156ddff
--- /dev/null
+++ b/libs/diskusage/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+    name: "libdiskusage",
+    srcs: ["dirsize.c"],
+}
diff --git a/libs/diskusage/Android.mk b/libs/diskusage/Android.mk
deleted file mode 100644
index d54f8ad..0000000
--- a/libs/diskusage/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libdiskusage
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := dirsize.c
-
-include $(BUILD_STATIC_LIBRARY)
\ No newline at end of file
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
new file mode 100644
index 0000000..a07726a
--- /dev/null
+++ b/libs/gui/Android.bp
@@ -0,0 +1,133 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libgui",
+
+    clang: true,
+    cppflags: [
+        "-Weverything",
+        "-Werror",
+
+        // The static constructors and destructors in this library have not been noted to
+        // introduce significant overheads
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+
+        // We only care about compiling as C++14
+        "-Wno-c++98-compat-pedantic",
+
+        // We don't need to enumerate every case in a switch as long as a default case
+        // is present
+        "-Wno-switch-enum",
+
+        // Allow calling variadic macros without a __VA_ARGS__ list
+        "-Wno-gnu-zero-variadic-macro-arguments",
+
+        // Don't warn about struct padding
+        "-Wno-padded",
+
+        // We are aware of the risks inherent in comparing floats for equality
+        "-Wno-float-equal",
+
+        // Pure abstract classes trigger this warning
+        "-Wno-weak-vtables",
+
+        // Allow four-character integer literals
+	"-Wno-four-char-constants",
+
+        // Allow documentation warnings
+        "-Wno-documentation",
+
+        "-DDEBUG_ONLY_CODE=0",
+    ],
+
+    product_variables: {
+        brillo: {
+            cflags: ["-DHAVE_NO_SURFACE_FLINGER"],
+        },
+        eng: {
+            cppflags: [
+                "-UDEBUG_ONLY_CODE",
+                "-DDEBUG_ONLY_CODE=1",
+            ],
+        },
+    },
+
+    srcs: [
+        "BitTube.cpp",
+        "BufferItem.cpp",
+        "BufferItemConsumer.cpp",
+        "BufferQueue.cpp",
+        "BufferQueueConsumer.cpp",
+        "BufferQueueCore.cpp",
+        "BufferQueueProducer.cpp",
+        "BufferSlot.cpp",
+        "ConsumerBase.cpp",
+        "CpuConsumer.cpp",
+        "DisplayEventReceiver.cpp",
+        "FrameTimestamps.cpp",
+        "GLConsumer.cpp",
+        "GuiConfig.cpp",
+        "IDisplayEventConnection.cpp",
+        "IConsumerListener.cpp",
+        "IGraphicBufferConsumer.cpp",
+        "IGraphicBufferProducer.cpp",
+        "IProducerListener.cpp",
+        "ISurfaceComposer.cpp",
+        "ISurfaceComposerClient.cpp",
+        "LayerState.cpp",
+        "OccupancyTracker.cpp",
+        "StreamSplitter.cpp",
+        "Surface.cpp",
+        "SurfaceControl.cpp",
+        "SurfaceComposerClient.cpp",
+        "SyncFeatures.cpp",
+        "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
+    ],
+
+    shared_libs: [
+        "libsync",
+        "libbinder",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libui",
+        "libutils",
+        "libnativewindow",
+        "liblog",
+        "libhidlbase",
+        "libhidltransport",
+        "android.hidl.base@1.0",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore-utils",
+    ],
+
+    export_shared_lib_headers: [
+        "libbinder",
+        "libui",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
deleted file mode 100644
index 9e2fc2b..0000000
--- a/libs/gui/Android.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -std=c++1y -Weverything -Werror
-
-# The static constructors and destructors in this library have not been noted to
-# introduce significant overheads
-LOCAL_CPPFLAGS += -Wno-exit-time-destructors
-LOCAL_CPPFLAGS += -Wno-global-constructors
-
-# We only care about compiling as C++14
-LOCAL_CPPFLAGS += -Wno-c++98-compat-pedantic
-
-# We don't need to enumerate every case in a switch as long as a default case
-# is present
-LOCAL_CPPFLAGS += -Wno-switch-enum
-
-# Allow calling variadic macros without a __VA_ARGS__ list
-LOCAL_CPPFLAGS += -Wno-gnu-zero-variadic-macro-arguments
-
-# Don't warn about struct padding
-LOCAL_CPPFLAGS += -Wno-padded
-
-LOCAL_CPPFLAGS += -DDEBUG_ONLY_CODE=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
-
-LOCAL_SRC_FILES := \
-	IGraphicBufferConsumer.cpp \
-	IConsumerListener.cpp \
-	BitTube.cpp \
-	BufferItem.cpp \
-	BufferItemConsumer.cpp \
-	BufferQueue.cpp \
-	BufferQueueConsumer.cpp \
-	BufferQueueCore.cpp \
-	BufferQueueProducer.cpp \
-	BufferSlot.cpp \
-	ConsumerBase.cpp \
-	CpuConsumer.cpp \
-	DisplayEventReceiver.cpp \
-	GLConsumer.cpp \
-	GraphicBufferAlloc.cpp \
-	GraphicsEnv.cpp \
-	GuiConfig.cpp \
-	IDisplayEventConnection.cpp \
-	IGraphicBufferAlloc.cpp \
-	IGraphicBufferProducer.cpp \
-	IProducerListener.cpp \
-	ISensorEventConnection.cpp \
-	ISensorServer.cpp \
-	ISurfaceComposer.cpp \
-	ISurfaceComposerClient.cpp \
-	LayerState.cpp \
-	OccupancyTracker.cpp \
-	Sensor.cpp \
-	SensorEventQueue.cpp \
-	SensorManager.cpp \
-	StreamSplitter.cpp \
-	Surface.cpp \
-	SurfaceControl.cpp \
-	SurfaceComposerClient.cpp \
-	SyncFeatures.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- 	libnativeloader \
-	libbinder \
-	libcutils \
-	libEGL \
-	libGLESv2 \
-	libsync \
-	libui \
-	libutils \
-	liblog
-
-
-LOCAL_MODULE := libgui
-
-ifeq ($(TARGET_BOARD_PLATFORM), tegra)
-	LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
-endif
-ifeq ($(TARGET_BOARD_PLATFORM), tegra3)
-	LOCAL_CFLAGS += -DDONT_USE_FENCE_SYNC
-endif
-
-include $(BUILD_SHARED_LIBRARY)
-
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index b653c5b..ef7a6f5 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+#include <private/gui/BitTube.h>
+
 #include <stdint.h>
-#include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -25,46 +27,21 @@
 
 #include <binder/Parcel.h>
 
-#include <gui/BitTube.h>
-
 namespace android {
-// ----------------------------------------------------------------------------
+namespace gui {
 
-// Socket buffer size.  The default is typically about 128KB, which is much larger than
-// we really need.  So we make it smaller.
+// Socket buffer size.  The default is typically about 128KB, which is much larger than we really
+// need. So we make it smaller.
 static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
 
-
-BitTube::BitTube()
-    : mSendFd(-1), mReceiveFd(-1)
-{
-    init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
-}
-
-BitTube::BitTube(size_t bufsize)
-    : mSendFd(-1), mReceiveFd(-1)
-{
+BitTube::BitTube(size_t bufsize) {
     init(bufsize, bufsize);
 }
 
-BitTube::BitTube(const Parcel& data)
-    : mSendFd(-1), mReceiveFd(-1)
-{
-    mReceiveFd = dup(data.readFileDescriptor());
-    if (mReceiveFd < 0) {
-        mReceiveFd = -errno;
-        ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
-                strerror(-mReceiveFd));
-    }
-}
+BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {}
 
-BitTube::~BitTube()
-{
-    if (mSendFd >= 0)
-        close(mSendFd);
-
-    if (mReceiveFd >= 0)
-        close(mReceiveFd);
+BitTube::BitTube(const Parcel& data) {
+    readFromParcel(&data);
 }
 
 void BitTube::init(size_t rcvbuf, size_t sndbuf) {
@@ -73,39 +50,43 @@
         size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
         setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
         setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
-        // sine we don't use the "return channel", we keep it small...
+        // since we don't use the "return channel", we keep it small...
         setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
         setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
         fcntl(sockets[0], F_SETFL, O_NONBLOCK);
         fcntl(sockets[1], F_SETFL, O_NONBLOCK);
-        mReceiveFd = sockets[0];
-        mSendFd = sockets[1];
+        mReceiveFd.reset(sockets[0]);
+        mSendFd.reset(sockets[1]);
     } else {
-        mReceiveFd = -errno;
-        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+        mReceiveFd.reset();
+        ALOGE("BitTube: pipe creation failed (%s)", strerror(errno));
     }
 }
 
-status_t BitTube::initCheck() const
-{
+status_t BitTube::initCheck() const {
     if (mReceiveFd < 0) {
         return status_t(mReceiveFd);
     }
     return NO_ERROR;
 }
 
-int BitTube::getFd() const
-{
+int BitTube::getFd() const {
     return mReceiveFd;
 }
 
-int BitTube::getSendFd() const
-{
+int BitTube::getSendFd() const {
     return mSendFd;
 }
 
-ssize_t BitTube::write(void const* vaddr, size_t size)
-{
+base::unique_fd BitTube::moveReceiveFd() {
+    return std::move(mReceiveFd);
+}
+
+void BitTube::setReceiveFd(base::unique_fd&& receiveFd) {
+    mReceiveFd = std::move(receiveFd);
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size) {
     ssize_t err, len;
     do {
         len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
@@ -115,62 +96,66 @@
     return err == 0 ? len : -err;
 }
 
-ssize_t BitTube::read(void* vaddr, size_t size)
-{
+ssize_t BitTube::read(void* vaddr, size_t size) {
     ssize_t err, len;
     do {
         len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
         err = len < 0 ? errno : 0;
     } while (err == EINTR);
     if (err == EAGAIN || err == EWOULDBLOCK) {
-        // EAGAIN means that we have non-blocking I/O but there was
-        // no data to be read. Nothing the client should care about.
+        // EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the
+        // client should care about.
         return 0;
     }
     return err == 0 ? len : -err;
 }
 
-status_t BitTube::writeToParcel(Parcel* reply) const
-{
-    if (mReceiveFd < 0)
-        return -EINVAL;
+status_t BitTube::writeToParcel(Parcel* reply) const {
+    if (mReceiveFd < 0) return -EINVAL;
 
     status_t result = reply->writeDupFileDescriptor(mReceiveFd);
-    close(mReceiveFd);
-    mReceiveFd = -1;
+    mReceiveFd.reset();
     return result;
 }
 
+status_t BitTube::readFromParcel(const Parcel* parcel) {
+    mReceiveFd.reset(dup(parcel->readFileDescriptor()));
+    if (mReceiveFd < 0) {
+        mReceiveFd.reset();
+        int error = errno;
+        ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
+        return -error;
+    }
+    return NO_ERROR;
+}
 
-ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
-        void const* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
     const char* vaddr = reinterpret_cast<const char*>(events);
-    ssize_t size = tube->write(vaddr, count*objSize);
+    ssize_t size = tube->write(vaddr, count * objSize);
 
     // should never happen because of SOCK_SEQPACKET
     LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
-            "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
-            count, objSize, size);
+                        "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were "
+                        "sent!)",
+                        count, objSize, size);
 
-    //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+    // ALOGE_IF(size<0, "error %d sending %d events", size, count);
     return size < 0 ? size : size / static_cast<ssize_t>(objSize);
 }
 
-ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
-        void* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
     char* vaddr = reinterpret_cast<char*>(events);
-    ssize_t size = tube->read(vaddr, count*objSize);
+    ssize_t size = tube->read(vaddr, count * objSize);
 
     // should never happen because of SOCK_SEQPACKET
     LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
-            "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
-            count, objSize, size);
+                        "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were "
+                        "received!)",
+                        count, objSize, size);
 
-    //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+    // ALOGE_IF(size<0, "error %d receiving %d events", size, count);
     return size < 0 ? size : size / static_cast<ssize_t>(objSize);
 }
 
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5e3924a..69b5962 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -23,6 +23,21 @@
 
 namespace android {
 
+template<typename T>
+static inline constexpr uint32_t low32(const T n) {
+    return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template<typename T>
+static inline constexpr uint32_t high32(const T n) {
+    return static_cast<uint32_t>(static_cast<uint64_t>(n)>>32);
+}
+
+template<typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+    return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
+}
+
 BufferItem::BufferItem() :
     mGraphicBuffer(NULL),
     mFence(NULL),
@@ -56,16 +71,19 @@
     addAligned(size, mCrop);
     addAligned(size, mTransform);
     addAligned(size, mScalingMode);
-    addAligned(size, mTimestampLo);
-    addAligned(size, mTimestampHi);
+    addAligned(size, low32(mTimestamp));
+    addAligned(size, high32(mTimestamp));
     addAligned(size, mIsAutoTimestamp);
     addAligned(size, mDataSpace);
-    addAligned(size, mFrameNumberLo);
-    addAligned(size, mFrameNumberHi);
+    addAligned(size, low32(mFrameNumber));
+    addAligned(size, high32(mFrameNumber));
     addAligned(size, mSlot);
     addAligned(size, mIsDroppable);
     addAligned(size, mAcquireCalled);
     addAligned(size, mTransformToDisplayInverse);
+    addAligned(size, mAutoRefresh);
+    addAligned(size, mQueuedBuffer);
+    addAligned(size, mIsStale);
     return size;
 }
 
@@ -73,11 +91,11 @@
     size_t size = sizeof(uint32_t); // Flags
     if (mGraphicBuffer != 0) {
         size += mGraphicBuffer->getFlattenedSize();
-        FlattenableUtils::align<4>(size);
+        size = FlattenableUtils::align<4>(size);
     }
     if (mFence != 0) {
         size += mFence->getFlattenedSize();
-        FlattenableUtils::align<4>(size);
+        size = FlattenableUtils::align<4>(size);
     }
     size += mSurfaceDamage.getFlattenedSize();
     size = FlattenableUtils::align<8>(size);
@@ -141,16 +159,19 @@
     writeAligned(buffer, size, mCrop);
     writeAligned(buffer, size, mTransform);
     writeAligned(buffer, size, mScalingMode);
-    writeAligned(buffer, size, mTimestampLo);
-    writeAligned(buffer, size, mTimestampHi);
+    writeAligned(buffer, size, low32(mTimestamp));
+    writeAligned(buffer, size, high32(mTimestamp));
     writeAligned(buffer, size, mIsAutoTimestamp);
     writeAligned(buffer, size, mDataSpace);
-    writeAligned(buffer, size, mFrameNumberLo);
-    writeAligned(buffer, size, mFrameNumberHi);
+    writeAligned(buffer, size, low32(mFrameNumber));
+    writeAligned(buffer, size, high32(mFrameNumber));
     writeAligned(buffer, size, mSlot);
     writeAligned(buffer, size, mIsDroppable);
     writeAligned(buffer, size, mAcquireCalled);
     writeAligned(buffer, size, mTransformToDisplayInverse);
+    writeAligned(buffer, size, mAutoRefresh);
+    writeAligned(buffer, size, mQueuedBuffer);
+    writeAligned(buffer, size, mIsStale);
 
     return NO_ERROR;
 }
@@ -183,6 +204,8 @@
         status_t err = mFence->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
+
+        mFenceTime = std::make_shared<FenceTime>(mFence);
     }
 
     status_t err = mSurfaceDamage.unflatten(buffer, size);
@@ -194,19 +217,27 @@
         return NO_MEMORY;
     }
 
+    uint32_t timestampLo = 0, timestampHi = 0;
+    uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
     readAligned(buffer, size, mCrop);
     readAligned(buffer, size, mTransform);
     readAligned(buffer, size, mScalingMode);
-    readAligned(buffer, size, mTimestampLo);
-    readAligned(buffer, size, mTimestampHi);
+    readAligned(buffer, size, timestampLo);
+    readAligned(buffer, size, timestampHi);
+    mTimestamp = to64<int64_t>(timestampLo, timestampHi);
     readAligned(buffer, size, mIsAutoTimestamp);
     readAligned(buffer, size, mDataSpace);
-    readAligned(buffer, size, mFrameNumberLo);
-    readAligned(buffer, size, mFrameNumberHi);
+    readAligned(buffer, size, frameNumberLo);
+    readAligned(buffer, size, frameNumberHi);
+    mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
     readAligned(buffer, size, mSlot);
     readAligned(buffer, size, mIsDroppable);
     readAligned(buffer, size, mAcquireCalled);
     readAligned(buffer, size, mTransformToDisplayInverse);
+    readAligned(buffer, size, mAutoRefresh);
+    readAligned(buffer, size, mQueuedBuffer);
+    readAligned(buffer, size, mIsStale);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 3491043..d9d50db 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -22,7 +22,7 @@
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 
-//#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
 //#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
 //#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
 //#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
@@ -57,6 +57,12 @@
     mConsumer->setConsumerName(name);
 }
 
+void BufferItemConsumer::setBufferFreedListener(
+        const wp<BufferFreedListener>& listener) {
+    Mutex::Autolock _l(mMutex);
+    mBufferFreedListener = listener;
+}
+
 status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
         nsecs_t presentWhen, bool waitForFence) {
     status_t err;
@@ -104,4 +110,14 @@
     return err;
 }
 
+void BufferItemConsumer::freeBufferLocked(int slotIndex) {
+    sp<BufferFreedListener> listener = mBufferFreedListener.promote();
+    if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) {
+        // Fire callback if we have a listener registered and the buffer being freed is valid.
+        BI_LOGV("actually calling onBufferFreed");
+        listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
+    }
+    ConsumerBase::freeBufferLocked(slotIndex);
+}
+
 } // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5..4151212 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -31,6 +31,13 @@
 
 BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
 
+void BufferQueue::ProxyConsumerListener::onDisconnect() {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != NULL) {
+        listener->onDisconnect();
+    }
+}
+
 void BufferQueue::ProxyConsumerListener::onFrameAvailable(
         const BufferItem& item) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
@@ -61,28 +68,28 @@
     }
 }
 
-bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
-        uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps(
+        const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta* outDelta) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
-        return listener->getFrameTimestamps(frameNumber, outTimestamps);
+    if (listener != nullptr) {
+        listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
     }
-    return false;
 }
 
 void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
         sp<IGraphicBufferConsumer>* outConsumer,
-        const sp<IGraphicBufferAlloc>& allocator) {
+        bool consumerIsSurfaceFlinger) {
     LOG_ALWAYS_FATAL_IF(outProducer == NULL,
             "BufferQueue: outProducer must not be NULL");
     LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
             "BufferQueue: outConsumer must not be NULL");
 
-    sp<BufferQueueCore> core(new BufferQueueCore(allocator));
+    sp<BufferQueueCore> core(new BufferQueueCore());
     LOG_ALWAYS_FATAL_IF(core == NULL,
             "BufferQueue: failed to create BufferQueueCore");
 
-    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
+    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
     LOG_ALWAYS_FATAL_IF(producer == NULL,
             "BufferQueue: failed to create BufferQueueProducer");
 
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 73d2042..5e5de44 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -205,6 +205,7 @@
             // was cached when it was last queued.
             outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
             outBuffer->mFence = Fence::NO_FENCE;
+            outBuffer->mFenceTime = FenceTime::NO_FENCE;
             outBuffer->mCrop = mCore->mSharedBufferCache.crop;
             outBuffer->mTransform = mCore->mSharedBufferCache.transform &
                     ~static_cast<uint32_t>(
@@ -259,7 +260,8 @@
         // decrease.
         mCore->mDequeueCondition.broadcast();
 
-        ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+        ATRACE_INT(mCore->mConsumerName.string(),
+                static_cast<int32_t>(mCore->mQueue.size()));
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
 
         VALIDATE_CONSISTENCY();
@@ -673,12 +675,13 @@
     return NO_ERROR;
 }
 
-void BufferQueueConsumer::setConsumerName(const String8& name) {
+status_t BufferQueueConsumer::setConsumerName(const String8& name) {
     ATRACE_CALL();
     BQ_LOGV("setConsumerName: '%s'", name.string());
     Mutex::Autolock lock(mCore->mMutex);
     mCore->mConsumerName = name;
     mConsumerName = name;
+    return NO_ERROR;
 }
 
 status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
@@ -706,6 +709,14 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) {
+    ATRACE_CALL();
+    BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false");
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mConsumerIsProtected = isProtected;
+    return NO_ERROR;
+}
+
 status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
     ATRACE_CALL();
     BQ_LOGV("setTransformHint: %#x", hint);
@@ -714,9 +725,10 @@
     return NO_ERROR;
 }
 
-sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
+status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const {
     Mutex::Autolock lock(mCore->mMutex);
-    return mCore->mSidebandStream;
+    *outStream = mCore->mSidebandStream;
+    return NO_ERROR;
 }
 
 status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
@@ -732,19 +744,22 @@
     return NO_ERROR;
 }
 
-void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
+status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
     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)) {
-        result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+        outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
                 "from pid=%d, uid=%d\n", pid, uid);
-        android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0);
-    } else {
-        mCore->dump(result, prefix);
+        android_errorWriteWithInfoLog(0x534e4554, "27046057",
+                static_cast<int32_t>(uid), NULL, 0);
+        return PERMISSION_DENIED;
     }
+
+    mCore->dumpState(prefix, outResult);
+    return NO_ERROR;
 }
 
 } // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index c4714e3..cfb25e0 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -28,10 +28,12 @@
 
 #include <inttypes.h>
 
+#include <cutils/properties.h>
+#include <cutils/atomic.h>
+
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
@@ -50,14 +52,14 @@
     return id | counter++;
 }
 
-BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
-    mAllocator(allocator),
+BufferQueueCore::BufferQueueCore() :
     mMutex(),
     mIsAbandoned(false),
     mConsumerControlledByApp(false),
     mConsumerName(getUniqueName()),
     mConsumerListener(),
     mConsumerUsageBits(0),
+    mConsumerIsProtected(false),
     mConnectedApi(NO_CONNECTED_API),
     mLinkedToDeath(),
     mConnectedProducerListener(),
@@ -93,14 +95,6 @@
     mLastQueuedSlot(INVALID_BUFFER_SLOT),
     mUniqueId(getUniqueId())
 {
-    if (allocator == NULL) {
-        sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-        mAllocator = composer->createGraphicBufferAlloc();
-        if (mAllocator == NULL) {
-            BQ_LOGE("createGraphicBufferAlloc failed");
-        }
-    }
-
     int numStartingBuffers = getMaxBufferCountLocked();
     for (int s = 0; s < numStartingBuffers; s++) {
         mFreeSlots.insert(s);
@@ -113,7 +107,7 @@
 
 BufferQueueCore::~BufferQueueCore() {}
 
-void BufferQueueCore::dump(String8& result, const char* prefix) const {
+void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
     Mutex::Autolock lock(mMutex);
 
     String8 fifo;
@@ -128,10 +122,10 @@
         ++current;
     }
 
-    result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
+    outResult->appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
             "mMaxDequeuedBufferCount=%d, mDequeueBufferCannotBlock=%d "
             "mAsyncMode=%d, default-size=[%dx%d], default-format=%d, "
-            "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix,
+            "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix.string(),
             mMaxAcquiredBufferCount, mMaxDequeuedBufferCount,
             mDequeueBufferCannotBlock, mAsyncMode, mDefaultWidth,
             mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
@@ -141,28 +135,28 @@
         const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
         // A dequeued buffer might be null if it's still being allocated
         if (buffer.get()) {
-            result.appendFormat("%s%s[%02d:%p] state=%-8s, %p "
-                    "[%4ux%4u:%4u,%3X]\n", prefix,
+            outResult->appendFormat("%s%s[%02d:%p] state=%-8s, %p "
+                    "[%4ux%4u:%4u,%3X]\n", prefix.string(),
                     (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
                     buffer.get(), mSlots[s].mBufferState.string(),
                     buffer->handle, buffer->width, buffer->height,
                     buffer->stride, buffer->format);
         } else {
-            result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+            outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
                     buffer.get(), mSlots[s].mBufferState.string());
         }
     }
     for (int s : mFreeBuffers) {
         const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
-        result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
-                prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+        outResult->appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+                prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string(),
                 buffer->handle, buffer->width, buffer->height, buffer->stride,
                 buffer->format);
     }
 
     for (int s : mFreeSlots) {
         const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
-        result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+        outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
                 buffer.get(), mSlots[s].mBufferState.string());
     }
 }
@@ -241,6 +235,13 @@
 
     for (auto& b : mQueue) {
         b.mIsStale = true;
+
+        // We set this to false to force the BufferQueue to resend the buffer
+        // handle upon acquire, since if we're here due to a producer
+        // disconnect, the consumer will have been told to purge its cache of
+        // slot-to-buffer-handle mappings and will not be able to otherwise
+        // obtain a valid buffer handle.
+        b.mAcquireCalled = false;
     }
 
     VALIDATE_CONSISTENCY();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index ff85eb5..8385864 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -34,7 +34,6 @@
 #include <gui/BufferQueueProducer.h>
 #include <gui/GLConsumer.h>
 #include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
 #include <gui/IProducerListener.h>
 
 #include <utils/Log.h>
@@ -42,12 +41,17 @@
 
 namespace android {
 
-BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+static constexpr uint32_t BQ_LAYER_COUNT = 1;
+
+BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
+        bool consumerIsSurfaceFlinger) :
     mCore(core),
     mSlots(core->mSlots),
     mConsumerName(),
     mStickyTransform(0),
+    mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
     mLastQueueBufferFence(Fence::NO_FENCE),
+    mLastQueuedTransform(0),
     mCallbackMutex(),
     mNextCallbackTicket(0),
     mCurrentCallbackTicket(0),
@@ -343,7 +347,8 @@
 
 status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
         sp<android::Fence> *outFence, uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t usage) {
+        PixelFormat format, uint32_t usage,
+        FrameEventHistoryDelta* outTimestamps) {
     ATRACE_CALL();
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
@@ -411,7 +416,8 @@
             // buffer. If this buffer would require reallocation to meet the
             // requested attributes, we free it and attempt to get another one.
             if (!mCore->mAllowAllocation) {
-                if (buffer->needsReallocation(width, height, format, usage)) {
+                if (buffer->needsReallocation(width, height, format,
+                        BQ_LAYER_COUNT, usage)) {
                     if (mCore->mSharedBufferSlot == found) {
                         BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                                 "buffer");
@@ -427,7 +433,8 @@
 
         const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if (mCore->mSharedBufferSlot == found &&
-                buffer->needsReallocation(width,  height, format, usage)) {
+                buffer->needsReallocation(width, height, format,
+                        BQ_LAYER_COUNT, usage)) {
             BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                     "buffer");
 
@@ -446,7 +453,7 @@
         mSlots[found].mBufferState.dequeue();
 
         if ((buffer == NULL) ||
-                buffer->needsReallocation(width, height, format, usage))
+                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
         {
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = NULL;
@@ -494,15 +501,17 @@
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
-        status_t error;
         BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
-        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
-                width, height, format, usage,
-                {mConsumerName.string(), mConsumerName.size()}, &error));
+        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                width, height, format, BQ_LAYER_COUNT, usage,
+                {mConsumerName.string(), mConsumerName.size()});
+
+        status_t error = graphicBuffer->initCheck();
+
         { // Autolock scope
             Mutex::Autolock lock(mCore->mMutex);
 
-            if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
+            if (error == NO_ERROR && !mCore->mIsAbandoned) {
                 graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                 mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
             }
@@ -510,7 +519,7 @@
             mCore->mIsAllocating = false;
             mCore->mIsAllocatingCondition.broadcast();
 
-            if (graphicBuffer == NULL) {
+            if (error != NO_ERROR) {
                 mCore->mFreeSlots.insert(*outSlot);
                 mCore->clearBufferSlotLocked(*outSlot);
                 BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
@@ -552,6 +561,8 @@
             mSlots[*outSlot].mFrameNumber,
             mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
 
+    addAndGetFrameTimestamps(nullptr, outTimestamps);
+
     return returnFlags;
 }
 
@@ -621,41 +632,49 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mCore->mMutex);
+    sp<IConsumerListener> listener;
+    {
+        Mutex::Autolock lock(mCore->mMutex);
 
-    if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
-        return NO_INIT;
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+            return NO_INIT;
+        }
+
+        if (mCore->mSharedBufferMode) {
+            BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
+                    "mode");
+            return BAD_VALUE;
+        }
+
+        mCore->waitWhileAllocatingLocked();
+
+        if (mCore->mFreeBuffers.empty()) {
+            return NO_MEMORY;
+        }
+
+        int found = mCore->mFreeBuffers.front();
+        mCore->mFreeBuffers.remove(found);
+        mCore->mFreeSlots.insert(found);
+
+        BQ_LOGV("detachNextBuffer detached slot %d", found);
+
+        *outBuffer = mSlots[found].mGraphicBuffer;
+        *outFence = mSlots[found].mFence;
+        mCore->clearBufferSlotLocked(found);
+        VALIDATE_CONSISTENCY();
+        listener = mCore->mConsumerListener;
     }
 
-    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
-        return NO_INIT;
+    if (listener != NULL) {
+        listener->onBuffersReleased();
     }
 
-    if (mCore->mSharedBufferMode) {
-        BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
-            "mode");
-        return BAD_VALUE;
-    }
-
-    mCore->waitWhileAllocatingLocked();
-
-    if (mCore->mFreeBuffers.empty()) {
-        return NO_MEMORY;
-    }
-
-    int found = mCore->mFreeBuffers.front();
-    mCore->mFreeBuffers.remove(found);
-    mCore->mFreeSlots.insert(found);
-
-    BQ_LOGV("detachNextBuffer detached slot %d", found);
-
-    *outBuffer = mSlots[found].mGraphicBuffer;
-    *outFence = mSlots[found].mFence;
-    mCore->clearBufferSlotLocked(found);
-    VALIDATE_CONSISTENCY();
-
     return NO_ERROR;
 }
 
@@ -721,6 +740,7 @@
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
     mSlots[*outSlot].mAcquireCalled = false;
+    mSlots[*outSlot].mNeedsReallocation = false;
     mCore->mActiveBuffers.insert(found);
     VALIDATE_CONSISTENCY();
 
@@ -732,23 +752,27 @@
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
 
-    int64_t timestamp;
+    int64_t requestedPresentTimestamp;
     bool isAutoTimestamp;
     android_dataspace dataSpace;
     Rect crop(Rect::EMPTY_RECT);
     int scalingMode;
     uint32_t transform;
     uint32_t stickyTransform;
-    sp<Fence> fence;
-    input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
-            &transform, &fence, &stickyTransform);
+    sp<Fence> acquireFence;
+    bool getFrameTimestamps = false;
+    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+            &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+            &getFrameTimestamps);
     Region surfaceDamage = input.getSurfaceDamage();
 
-    if (fence == NULL) {
+    if (acquireFence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
         return BAD_VALUE;
     }
 
+    auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
+
     switch (scalingMode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
@@ -763,6 +787,7 @@
     sp<IConsumerListener> frameAvailableListener;
     sp<IConsumerListener> frameReplacedListener;
     int callbackTicket = 0;
+    uint64_t currentFrameNumber = 0;
     BufferItem item;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
@@ -801,8 +826,9 @@
 
         BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
                 " crop=[%d,%d,%d,%d] transform=%#x scale=%s",
-                slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
-                crop.left, crop.top, crop.right, crop.bottom, transform,
+                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+                dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+                transform,
                 BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
 
         const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -820,11 +846,14 @@
             dataSpace = mCore->mDefaultBufferDataSpace;
         }
 
-        mSlots[slot].mFence = fence;
+        mSlots[slot].mFence = acquireFence;
         mSlots[slot].mBufferState.queue();
 
+        // Increment the frame counter and store a local version of it
+        // for use outside the lock on mCore->mMutex.
         ++mCore->mFrameCounter;
-        mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+        currentFrameNumber = mCore->mFrameCounter;
+        mSlots[slot].mFrameNumber = currentFrameNumber;
 
         item.mAcquireCalled = mSlots[slot].mAcquireCalled;
         item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -834,12 +863,13 @@
         item.mTransformToDisplayInverse =
                 (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
         item.mScalingMode = static_cast<uint32_t>(scalingMode);
-        item.mTimestamp = timestamp;
+        item.mTimestamp = requestedPresentTimestamp;
         item.mIsAutoTimestamp = isAutoTimestamp;
         item.mDataSpace = dataSpace;
-        item.mFrameNumber = mCore->mFrameCounter;
+        item.mFrameNumber = currentFrameNumber;
         item.mSlot = slot;
-        item.mFence = fence;
+        item.mFence = acquireFence;
+        item.mFenceTime = acquireFenceTime;
         item.mIsDroppable = mCore->mAsyncMode ||
                 mCore->mDequeueBufferCannotBlock ||
                 (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -858,6 +888,7 @@
             mCore->mSharedBufferCache.dataspace = dataSpace;
         }
 
+        output->bufferReplaced = false;
         if (mCore->mQueue.empty()) {
             // When the queue is empty, we can ignore mDequeueBufferCannotBlock
             // and simply queue this buffer
@@ -884,6 +915,7 @@
                     if (!mSlots[last.mSlot].mBufferState.isShared()) {
                         mCore->mActiveBuffers.erase(last.mSlot);
                         mCore->mFreeBuffers.push_back(last.mSlot);
+                        output->bufferReplaced = true;
                     }
                 }
 
@@ -900,12 +932,14 @@
         mCore->mDequeueCondition.broadcast();
         mCore->mLastQueuedSlot = slot;
 
-        output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
-                mCore->mTransformHint,
-                static_cast<uint32_t>(mCore->mQueue.size()),
-                mCore->mFrameCounter + 1);
+        output->width = mCore->mDefaultWidth;
+        output->height = mCore->mDefaultHeight;
+        output->transformHint = mCore->mTransformHint;
+        output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
+        output->nextFrameNumber = mCore->mFrameCounter + 1;
 
-        ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+        ATRACE_INT(mCore->mConsumerName.string(),
+                static_cast<int32_t>(mCore->mQueue.size()));
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
 
         // Take a ticket for the callback functions
@@ -914,9 +948,14 @@
         VALIDATE_CONSISTENCY();
     } // Autolock scope
 
-    // Don't send the GraphicBuffer through the callback, and don't send
-    // the slot number, since the consumer shouldn't need it
-    item.mGraphicBuffer.clear();
+    // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
+    // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
+    // there will be no Binder call
+    if (!mConsumerIsSurfaceFlinger) {
+        item.mGraphicBuffer.clear();
+    }
+
+    // Don't send the slot number through the callback since the consumer shouldn't need it
     item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
 
     // Call back without the main BufferQueue lock held, but with the callback
@@ -940,7 +979,7 @@
         connectedApi = mCore->mConnectedApi;
         lastQueuedFence = std::move(mLastQueueBufferFence);
 
-        mLastQueueBufferFence = std::move(fence);
+        mLastQueueBufferFence = std::move(acquireFence);
         mLastQueuedCrop = item.mCrop;
         mLastQueuedTransform = item.mTransform;
 
@@ -956,6 +995,17 @@
         lastQueuedFence->waitForever("Throttling EGL Production");
     }
 
+    // Update and get FrameEventHistory.
+    nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    NewFrameEventsEntry newFrameEventsEntry = {
+        currentFrameNumber,
+        postedTime,
+        requestedPresentTimestamp,
+        std::move(acquireFenceTime)
+    };
+    addAndGetFrameTimestamps(&newFrameEventsEntry,
+            getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
     return NO_ERROR;
 }
 
@@ -1038,6 +1088,10 @@
         case NATIVE_WINDOW_FORMAT:
             value = static_cast<int32_t>(mCore->mDefaultBufferFormat);
             break;
+        case NATIVE_WINDOW_LAYER_COUNT:
+            // All BufferQueue buffers have a single layer.
+            value = BQ_LAYER_COUNT;
+            break;
         case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
             value = mCore->getMinUndequeuedBufferCountLocked();
             break;
@@ -1060,6 +1114,9 @@
                 value = static_cast<int32_t>(mCore->mBufferAge);
             }
             break;
+        case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+            value = static_cast<int32_t>(mCore->mConsumerIsProtected);
+            break;
         default:
             return BAD_VALUE;
     }
@@ -1116,10 +1173,14 @@
         case NATIVE_WINDOW_API_MEDIA:
         case NATIVE_WINDOW_API_CAMERA:
             mCore->mConnectedApi = api;
-            output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
-                    mCore->mTransformHint,
-                    static_cast<uint32_t>(mCore->mQueue.size()),
-                    mCore->mFrameCounter + 1);
+
+            output->width = mCore->mDefaultWidth;
+            output->height = mCore->mDefaultHeight;
+            output->transformHint = mCore->mTransformHint;
+            output->numPendingBuffers =
+                    static_cast<uint32_t>(mCore->mQueue.size());
+            output->nextFrameNumber = mCore->mFrameCounter + 1;
+            output->bufferReplaced = false;
 
             if (listener != NULL) {
                 // Set up a death notification so that we can disconnect
@@ -1181,6 +1242,9 @@
         }
 
         if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
+            if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
+                ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
+            }
             api = mCore->mConnectedApi;
             // If we're asked to disconnect the currently connected api but
             // nobody is connected, it's not really an error.
@@ -1215,7 +1279,10 @@
                     mCore->mSidebandStream.clear();
                     mCore->mDequeueCondition.broadcast();
                     listener = mCore->mConsumerListener;
-                } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+                } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+                    BQ_LOGE("disconnect: not connected (req=%d)", api);
+                    status = NO_INIT;
+                } else {
                     BQ_LOGE("disconnect: still connected to another API "
                             "(cur=%d req=%d)", mCore->mConnectedApi, api);
                     status = BAD_VALUE;
@@ -1231,6 +1298,7 @@
     // Call back without lock held
     if (listener != NULL) {
         listener->onBuffersReleased();
+        listener->onDisconnect();
     }
 
     return status;
@@ -1284,10 +1352,12 @@
 
         Vector<sp<GraphicBuffer>> buffers;
         for (size_t i = 0; i <  newBufferCount; ++i) {
-            status_t result = NO_ERROR;
-            sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
-                    allocWidth, allocHeight, allocFormat, allocUsage,
-                    {mConsumerName.string(), mConsumerName.size()}, &result));
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                    allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                    allocUsage, {mConsumerName.string(), mConsumerName.size()});
+
+            status_t result = graphicBuffer->initCheck();
+
             if (result != NO_ERROR) {
                 BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
                         " %u, usage %u)", width, height, format, usage);
@@ -1438,20 +1508,27 @@
     return NO_ERROR;
 }
 
-bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
-        FrameTimestamps* outTimestamps) const {
-    ATRACE_CALL();
-    BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
-    sp<IConsumerListener> listener;
+void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+    addAndGetFrameTimestamps(nullptr, outDelta);
+}
 
+void BufferQueueProducer::addAndGetFrameTimestamps(
+        const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta* outDelta) {
+    if (newTimestamps == nullptr && outDelta == nullptr) {
+        return;
+    }
+
+    ATRACE_CALL();
+    BQ_LOGV("addAndGetFrameTimestamps");
+    sp<IConsumerListener> listener;
     {
         Mutex::Autolock lock(mCore->mMutex);
         listener = mCore->mConsumerListener;
     }
     if (listener != NULL) {
-        return listener->getFrameTimestamps(frameNumber, outTimestamps);
+        listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
     }
-    return false;
 }
 
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 65e4fee..3d36376 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -27,8 +27,9 @@
 
 #include <hardware/hardware.h>
 
+#include <cutils/atomic.h>
+
 #include <gui/BufferItem.h>
-#include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/ConsumerBase.h>
@@ -56,7 +57,8 @@
 
 ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
         mAbandoned(false),
-        mConsumer(bufferQueue) {
+        mConsumer(bufferQueue),
+        mPrevFinalReleaseFence(Fence::NO_FENCE) {
     // Choose a name using the PID and a process-unique ID.
     mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
 
@@ -104,7 +106,7 @@
 
     sp<FrameAvailableListener> listener;
     { // scope for the lock
-        Mutex::Autolock lock(mMutex);
+        Mutex::Autolock lock(mFrameAvailableMutex);
         listener = mFrameAvailableListener.promote();
     }
 
@@ -119,7 +121,7 @@
 
     sp<FrameAvailableListener> listener;
     {
-        Mutex::Autolock lock(mMutex);
+        Mutex::Autolock lock(mFrameAvailableMutex);
         listener = mFrameAvailableListener.promote();
     }
 
@@ -183,7 +185,7 @@
 void ConsumerBase::setFrameAvailableListener(
         const wp<FrameAvailableListener>& listener) {
     CB_LOGV("setFrameAvailableListener");
-    Mutex::Autolock lock(mMutex);
+    Mutex::Autolock lock(mFrameAvailableMutex);
     mFrameAvailableListener = listener;
 }
 
@@ -251,14 +253,25 @@
         CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
         return NO_INIT;
     }
-    return mConsumer->discardFreeBuffers();
+    status_t err = mConsumer->discardFreeBuffers();
+    if (err != OK) {
+        return err;
+    }
+    uint64_t mask;
+    mConsumer->getReleasedBuffers(&mask);
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if (mask & (1ULL << i)) {
+            freeBufferLocked(i);
+        }
+    }
+    return OK;
 }
 
-void ConsumerBase::dump(String8& result) const {
-    dump(result, "");
+void ConsumerBase::dumpState(String8& result) const {
+    dumpState(result, "");
 }
 
-void ConsumerBase::dump(String8& result, const char* prefix) const {
+void ConsumerBase::dumpState(String8& result, const char* prefix) const {
     Mutex::Autolock _l(mMutex);
     dumpLocked(result, prefix);
 }
@@ -267,7 +280,9 @@
     result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
 
     if (!mAbandoned) {
-        mConsumer->dump(result, prefix);
+        String8 consumerState;
+        mConsumer->dumpState(String8(prefix), &consumerState);
+        result.append(consumerState);
     }
 }
 
@@ -284,6 +299,9 @@
     }
 
     if (item->mGraphicBuffer != NULL) {
+        if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+            freeBufferLocked(item->mSlot);
+        }
         mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
     }
 
@@ -317,16 +335,16 @@
         return OK;
     }
 
-    auto signaled = mSlots[slot].mFence->hasSignaled();
+    auto status = mSlots[slot].mFence->getStatus();
 
-    if (!signaled) {
+    if (status == Fence::Status::Invalid) {
         CB_LOGE("fence has invalid state");
         return BAD_VALUE;
     }
 
-    if (*signaled) {
+    if (status == Fence::Status::Signaled) {
         mSlots[slot].mFence = fence;
-    } else {
+    } else {  // status == Fence::Status::Unsignaled
         char fenceName[32] = {};
         snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
         sp<Fence> mergedFence = Fence::merge(
@@ -366,6 +384,7 @@
         freeBufferLocked(slot);
     }
 
+    mPrevFinalReleaseFence = mSlots[slot].mFence;
     mSlots[slot].mFence = Fence::NO_FENCE;
 
     return err;
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8393160..ae7c65c 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,8 @@
     switch (static_cast<int>(format)) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 9973e8d..1757ec1 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -18,25 +18,27 @@
 
 #include <utils/Errors.h>
 
-#include <gui/BitTube.h>
 #include <gui/DisplayEventReceiver.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/ISurfaceComposer.h>
 
 #include <private/gui/ComposerService.h>
 
+#include <private/gui/BitTube.h>
+
 // ---------------------------------------------------------------------------
 
 namespace android {
 
 // ---------------------------------------------------------------------------
 
-DisplayEventReceiver::DisplayEventReceiver() {
+DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     if (sf != NULL) {
-        mEventConnection = sf->createDisplayEventConnection();
+        mEventConnection = sf->createDisplayEventConnection(vsyncSource);
         if (mEventConnection != NULL) {
-            mDataChannel = mEventConnection->getDataChannel();
+            mDataChannel = std::make_unique<gui::BitTube>();
+            mEventConnection->stealReceiveChannel(mDataChannel.get());
         }
     }
 }
@@ -79,19 +81,19 @@
 
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
-    return DisplayEventReceiver::getEvents(mDataChannel, events, count);
+    return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
 }
 
-ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
         Event* events, size_t count)
 {
-    return BitTube::recvObjects(dataChannel, events, count);
+    return gui::BitTube::recvObjects(dataChannel, events, count);
 }
 
-ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
         Event const* events, size_t count)
 {
-    return BitTube::sendObjects(dataChannel, events, count);
+    return gui::BitTube::sendObjects(dataChannel, events, count);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..fccca97
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,701 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <gui/FrameTimestamps.h>
+
+#define LOG_TAG "FrameEvents"
+
+#include <cutils/compiler.h>  // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+    return FrameEvents::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+    return FrameEvents::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+    return FrameEvents::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+    return FrameEvents::isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+    // The last refresh start time may continue to update until a new frame
+    // is latched. We know we have the final value once the release info is set.
+    return addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+    return FrameEvents::isValidTimestamp(dequeueReadyTime);
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+    return acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+    // We may not get a gpuCompositionDone in addPostComposite if
+    // client/gles compositing isn't needed.
+    return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+    // We may not get a displayPresent in addPostComposite for HWC1.
+    return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+    return addReleaseCalled;
+}
+
+void FrameEvents::checkFencesForCompletion() {
+    acquireFence->getSignalTime();
+    gpuCompositionDoneFence->getSignalTime();
+    displayPresentFence->getSignalTime();
+    releaseFence->getSignalTime();
+}
+
+static void dumpFenceTime(String8& outString, const char* name,
+        bool pending, const FenceTime& fenceTime) {
+    outString.appendFormat("--- %s", name);
+    nsecs_t signalTime = fenceTime.getCachedSignalTime();
+    if (Fence::isValidTimestamp(signalTime)) {
+        outString.appendFormat("%" PRId64 "\n", signalTime);
+    } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
+        outString.appendFormat("Pending\n");
+    } else if (&fenceTime == FenceTime::NO_FENCE.get()){
+        outString.appendFormat("N/A\n");
+    } else {
+        outString.appendFormat("Error\n");
+    }
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+    if (!valid) {
+        return;
+    }
+
+    outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+    outString.appendFormat("--- Posted      \t%" PRId64 "\n", postedTime);
+    outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+    outString.appendFormat("--- Latched     \t");
+    if (FrameEvents::isValidTimestamp(latchTime)) {
+        outString.appendFormat("%" PRId64 "\n", latchTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (First)\t");
+    if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
+        outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (Last)\t");
+    if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
+        outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    dumpFenceTime(outString, "Acquire           \t",
+            true, *acquireFence);
+    dumpFenceTime(outString, "GPU Composite Done\t",
+            !addPostCompositeCalled, *gpuCompositionDoneFence);
+    dumpFenceTime(outString, "Display Present   \t",
+            !addPostCompositeCalled, *displayPresentFence);
+
+    outString.appendFormat("--- DequeueReady  \t");
+    if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
+        outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    dumpFenceTime(outString, "Release           \t",
+            true, *releaseFence);
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+    FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+    bool operator()(const FrameEvents& frame) {
+        return frame.valid && mFrameNumber == frame.frameNumber;
+    }
+    const uint64_t mFrameNumber;
+};
+
+}  // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+    auto frame = std::find_if(
+            mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+    return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+    *iHint = std::min(*iHint, mFrames.size());
+    auto hint = mFrames.begin() + *iHint;
+    auto frame = std::find_if(
+            hint, mFrames.end(), FrameNumberEqual(frameNumber));
+    if (frame == mFrames.end()) {
+        frame = std::find_if(
+                mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+        if (frame == hint) {
+            return nullptr;
+        }
+    }
+    *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+    return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+    for (auto& frame : mFrames) {
+        frame.checkFencesForCompletion();
+    }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+        const FrameEvents& lhs, const FrameEvents& rhs) {
+    if (lhs.valid == rhs.valid) {
+        return lhs.frameNumber < rhs.frameNumber;
+    }
+    return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+    auto earliestFrame = std::min_element(
+            mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+    if (!earliestFrame->valid) {
+        outString.appendFormat("-- N/A\n");
+        return;
+    }
+    for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+        frame->dump(outString);
+    }
+    for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+        frame->dump(outString);
+    }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+nsecs_t ProducerFrameEventHistory::snapToNextTick(
+        nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) {
+    nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval;
+    // Integer modulo rounds towards 0 and not -inf before taking the remainder,
+    // so adjust the offset if it is negative.
+    if (tickOffset < 0) {
+        tickOffset += tickInterval;
+    }
+    return timestamp + tickOffset;
+}
+
+nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline(
+        const nsecs_t now) const{
+    return snapToNextTick(
+            now, mCompositorTiming.deadline, mCompositorTiming.interval);
+}
+
+void ProducerFrameEventHistory::updateAcquireFence(
+        uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+    FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+    if (frame == nullptr) {
+        ALOGE("updateAcquireFence: Did not find frame.");
+        return;
+    }
+
+    if (acquire->isValid()) {
+        mAcquireTimeline.push(acquire);
+        frame->acquireFence = std::move(acquire);
+    } else {
+        // If there isn't an acquire fence, assume that buffer was
+        // ready for the consumer when posted.
+        frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime);
+    }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+        const FrameEventHistoryDelta& delta) {
+    mCompositorTiming = delta.mCompositorTiming;
+
+    for (auto& d : delta.mDeltas) {
+        // Avoid out-of-bounds access.
+        if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
+            ALOGE("applyDelta: Bad index.");
+            return;
+        }
+
+        FrameEvents& frame = mFrames[d.mIndex];
+
+        frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+        frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+        frame.postedTime = d.mPostedTime;
+        frame.requestedPresentTime = d.mRequestedPresentTime;
+        frame.latchTime = d.mLatchTime;
+        frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
+        frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+        frame.dequeueReadyTime = d.mDequeueReadyTime;
+
+        if (frame.frameNumber != d.mFrameNumber) {
+            // We got a new frame. Initialize some of the fields.
+            frame.frameNumber = d.mFrameNumber;
+            frame.acquireFence = FenceTime::NO_FENCE;
+            frame.gpuCompositionDoneFence = FenceTime::NO_FENCE;
+            frame.displayPresentFence = FenceTime::NO_FENCE;
+            frame.releaseFence = FenceTime::NO_FENCE;
+            // The consumer only sends valid frames.
+            frame.valid = true;
+        }
+
+        applyFenceDelta(&mGpuCompositionDoneTimeline,
+                &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+        applyFenceDelta(&mPresentTimeline,
+                &frame.displayPresentFence, d.mDisplayPresentFence);
+        applyFenceDelta(&mReleaseTimeline,
+                &frame.releaseFence, d.mReleaseFence);
+    }
+}
+
+void ProducerFrameEventHistory::updateSignalTimes() {
+    mAcquireTimeline.updateSignalTimes();
+    mGpuCompositionDoneTimeline.updateSignalTimes();
+    mPresentTimeline.updateSignalTimes();
+    mReleaseTimeline.updateSignalTimes();
+}
+
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+        std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+    if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) {
+        ALOGE("applyFenceDelta: dst is null.");
+        return;
+    }
+
+    switch (src.state) {
+        case FenceTime::Snapshot::State::EMPTY:
+            return;
+        case FenceTime::Snapshot::State::FENCE:
+            ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence.");
+            *dst = createFenceTime(src.fence);
+            timeline->push(*dst);
+            return;
+        case FenceTime::Snapshot::State::SIGNAL_TIME:
+            if ((*dst)->isValid()) {
+                (*dst)->applyTrustedSnapshot(src);
+            } else {
+                *dst = std::make_shared<FenceTime>(src.signalTime);
+            }
+            return;
+    }
+}
+
+std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
+        const sp<Fence>& fence) const {
+    return std::make_shared<FenceTime>(fence);
+}
+
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::onDisconnect() {
+    mCurrentConnectId++;
+    mProducerWantsEvents = false;
+}
+
+void ConsumerFrameEventHistory::initializeCompositorTiming(
+        const CompositorTiming& compositorTiming) {
+    mCompositorTiming = compositorTiming;
+}
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+    // Overwrite all fields of the frame with default values unless set here.
+    FrameEvents newTimestamps;
+    newTimestamps.connectId = mCurrentConnectId;
+    newTimestamps.frameNumber = newEntry.frameNumber;
+    newTimestamps.postedTime = newEntry.postedTime;
+    newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+    newTimestamps.acquireFence = newEntry.acquireFence;
+    newTimestamps.valid = true;
+    mFrames[mQueueOffset] = newTimestamps;
+
+    // Note: We avoid sending the acquire fence back to the caller since
+    // they have the original one already, so there is no need to set the
+    // acquire dirty bit.
+    mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+    mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+        uint64_t frameNumber, nsecs_t latchTime) {
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame.");
+        return;
+    }
+    frame->latchTime = latchTime;
+    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+        uint64_t frameNumber, nsecs_t refreshStartTime) {
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents,
+                "addPreComposition: Did not find frame.");
+        return;
+    }
+    frame->lastRefreshStartTime = refreshStartTime;
+    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+    if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) {
+        frame->firstRefreshStartTime = refreshStartTime;
+        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+    }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+        const std::shared_ptr<FenceTime>& gpuCompositionDone,
+        const std::shared_ptr<FenceTime>& displayPresent,
+        const CompositorTiming& compositorTiming) {
+    mCompositorTiming = compositorTiming;
+
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents,
+                "addPostComposition: Did not find frame.");
+        return;
+    }
+    // Only get GPU and present info for the first composite.
+    if (!frame->addPostCompositeCalled) {
+        frame->addPostCompositeCalled = true;
+        frame->gpuCompositionDoneFence = gpuCompositionDone;
+        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GPU_COMPOSITION_DONE>();
+        if (!frame->displayPresentFence->isValid()) {
+            frame->displayPresentFence = displayPresent;
+            mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+        }
+    }
+}
+
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+        nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
+    FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame.");
+        return;
+    }
+    frame->addReleaseCalled = true;
+    frame->dequeueReadyTime = dequeueReadyTime;
+    frame->releaseFence = std::move(release);
+    mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getFrameDelta(
+        FrameEventHistoryDelta* delta,
+        const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+    mProducerWantsEvents = true;
+    size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+    if (mFramesDirty[i].anyDirty()) {
+        // Make sure only to send back deltas for the current connection
+        // since the producer won't have the correct state to apply a delta
+        // from a previous connection.
+        if (mFrames[i].connectId == mCurrentConnectId) {
+            delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+        }
+        mFramesDirty[i].reset();
+    }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+        FrameEventHistoryDelta* delta) {
+    mProducerWantsEvents = true;
+    delta->mCompositorTiming = mCompositorTiming;
+
+    // Write these in order of frame number so that it is easy to
+    // add them to a FenceTimeline in the proper order producer side.
+    delta->mDeltas.reserve(mFramesDirty.size());
+    auto earliestFrame = std::min_element(
+            mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+    for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+        getFrameDelta(delta, frame);
+    }
+    for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+        getFrameDelta(delta, frame);
+    }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+        size_t index,
+        const FrameEvents& frameTimestamps,
+        const FrameEventDirtyFields& dirtyFields)
+    : mIndex(index),
+      mFrameNumber(frameTimestamps.frameNumber),
+      mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+      mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+      mPostedTime(frameTimestamps.postedTime),
+      mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+      mLatchTime(frameTimestamps.latchTime),
+      mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+      mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime),
+      mDequeueReadyTime(frameTimestamps.dequeueReadyTime) {
+    if (dirtyFields.isDirty<FrameEvent::GPU_COMPOSITION_DONE>()) {
+        mGpuCompositionDoneFence =
+                frameTimestamps.gpuCompositionDoneFence->getSnapshot();
+    }
+    if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
+        mDisplayPresentFence =
+                frameTimestamps.displayPresentFence->getSnapshot();
+    }
+    if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
+        mReleaseFence = frameTimestamps.releaseFence->getSnapshot();
+    }
+}
+
+constexpr size_t FrameEventsDelta::minFlattenedSize() {
+    return sizeof(FrameEventsDelta::mFrameNumber) +
+            sizeof(uint16_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(FrameEventsDelta::mPostedTime) +
+            sizeof(FrameEventsDelta::mRequestedPresentTime) +
+            sizeof(FrameEventsDelta::mLatchTime) +
+            sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+            sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+            sizeof(FrameEventsDelta::mDequeueReadyTime);
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+    auto fences = allFences(this);
+    return minFlattenedSize() +
+            std::accumulate(fences.begin(), fences.end(), size_t(0),
+                    [](size_t a, const FenceTime::Snapshot* fence) {
+                            return a + fence->getFlattenedSize();
+                    });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+    auto fences = allFences(this);
+    return std::accumulate(fences.begin(), fences.end(), size_t(0),
+            [](size_t a, const FenceTime::Snapshot* fence) {
+                return a + fence->getFdCount();
+            });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+            size_t& count) const {
+    if (size < getFlattenedSize() || count < getFdCount()) {
+        return NO_MEMORY;
+    }
+
+    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+            mIndex > std::numeric_limits<uint16_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, mFrameNumber);
+
+    // These are static_cast to uint16_t/uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint16_t>(mIndex));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+    FlattenableUtils::write(buffer, size, mPostedTime);
+    FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+    FlattenableUtils::write(buffer, size, mLatchTime);
+    FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
+    FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+    FlattenableUtils::write(buffer, size, mDequeueReadyTime);
+
+    // Fences
+    for (auto fence : allFences(this)) {
+        status_t status = fence->flatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+            int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mFrameNumber);
+
+    // These were written as uint16_t/uint8_t for alignment.
+    uint16_t temp16 = 0;
+    FlattenableUtils::read(buffer, size, temp16);
+    mIndex = temp16;
+    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    uint8_t temp8 = 0;
+    FlattenableUtils::read(buffer, size, temp8);
+    mAddPostCompositeCalled = static_cast<bool>(temp8);
+    FlattenableUtils::read(buffer, size, temp8);
+    mAddReleaseCalled = static_cast<bool>(temp8);
+
+    FlattenableUtils::read(buffer, size, mPostedTime);
+    FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+    FlattenableUtils::read(buffer, size, mLatchTime);
+    FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
+    FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+    FlattenableUtils::read(buffer, size, mDequeueReadyTime);
+
+    // Fences
+    for (auto fence : allFences(this)) {
+        status_t status = fence->unflatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+FrameEventHistoryDelta& FrameEventHistoryDelta::operator=(
+        FrameEventHistoryDelta&& src) {
+    mCompositorTiming = src.mCompositorTiming;
+
+    if (CC_UNLIKELY(!mDeltas.empty())) {
+        ALOGE("FrameEventHistoryDelta assign clobbering history.");
+    }
+    mDeltas = std::move(src.mDeltas);
+    ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+    return *this;
+}
+
+constexpr size_t FrameEventHistoryDelta::minFlattenedSize() {
+    return sizeof(uint32_t) + // mDeltas.size()
+            sizeof(mCompositorTiming);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+    return minFlattenedSize() +
+            std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+                    [](size_t a, const FrameEventsDelta& delta) {
+                            return a + delta.getFlattenedSize();
+                    });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+    return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+            [](size_t a, const FrameEventsDelta& delta) {
+                    return a + delta.getFdCount();
+            });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+            void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, mCompositorTiming);
+
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint32_t>(mDeltas.size()));
+    for (auto& d : mDeltas) {
+        status_t status = d.flatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+            void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mCompositorTiming);
+
+    uint32_t deltaCount = 0;
+    FlattenableUtils::read(buffer, size, deltaCount);
+    if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    mDeltas.resize(deltaCount);
+    for (auto& d : mDeltas) {
+        status_t status = d.unflatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 10e999c..c654f08 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -31,7 +31,6 @@
 
 #include <gui/BufferItem.h>
 #include <gui/GLConsumer.h>
-#include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 
@@ -157,6 +156,7 @@
     mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
     mCurrentFence(Fence::NO_FENCE),
     mCurrentTimestamp(0),
+    mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
     mCurrentFrameNumber(0),
     mDefaultWidth(1),
     mDefaultHeight(1),
@@ -185,6 +185,7 @@
     mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
     mCurrentFence(Fence::NO_FENCE),
     mCurrentTimestamp(0),
+    mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
     mCurrentFrameNumber(0),
     mDefaultWidth(1),
     mDefaultHeight(1),
@@ -321,7 +322,9 @@
         mCurrentCrop.makeInvalid();
         mCurrentTransform = 0;
         mCurrentTimestamp = 0;
+        mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
         mCurrentFence = Fence::NO_FENCE;
+        mCurrentFenceTime = FenceTime::NO_FENCE;
 
         if (mAttached) {
             // This binds a dummy buffer (mReleasedTexImage).
@@ -487,7 +490,9 @@
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
     mCurrentTimestamp = item.mTimestamp;
+    mCurrentDataSpace = item.mDataSpace;
     mCurrentFence = item.mFence;
+    mCurrentFenceTime = item.mFenceTime;
     mCurrentFrameNumber = item.mFrameNumber;
 
     computeCurrentTransformMatrixLocked();
@@ -856,6 +861,8 @@
             switch (buf->getPixelFormat()) {
                 case PIXEL_FORMAT_RGBA_8888:
                 case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGBA_FP16:
+                case PIXEL_FORMAT_RGBA_1010102:
                 case PIXEL_FORMAT_RGB_888:
                 case PIXEL_FORMAT_RGB_565:
                 case PIXEL_FORMAT_BGRA_8888:
@@ -911,15 +918,26 @@
     return mCurrentTimestamp;
 }
 
+android_dataspace GLConsumer::getCurrentDataSpace() {
+    GLC_LOGV("getCurrentDataSpace");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentDataSpace;
+}
+
 uint64_t GLConsumer::getFrameNumber() {
     GLC_LOGV("getFrameNumber");
     Mutex::Autolock lock(mMutex);
     return mCurrentFrameNumber;
 }
 
-sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const {
     Mutex::Autolock lock(mMutex);
-    return (mCurrentTextureImage == NULL) ?
+
+    if (outSlot != nullptr) {
+        *outSlot = mCurrentTexture;
+    }
+
+    return (mCurrentTextureImage == nullptr) ?
             NULL : mCurrentTextureImage->graphicBuffer();
 }
 
@@ -981,6 +999,11 @@
     return mCurrentFence;
 }
 
+std::shared_ptr<FenceTime> GLConsumer::getCurrentFenceTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFenceTime;
+}
+
 status_t GLConsumer::doGLFenceWait() const {
     Mutex::Autolock lock(mMutex);
     return doGLFenceWaitLocked();
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
deleted file mode 100644
index e6150f4..0000000
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- **
- ** Copyright 2012 The Android Open Source Project
- **
- ** Licensed under the Apache License Version 2.0(the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing software
- ** distributed under the License is distributed on an "AS IS" BASIS
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#include <cutils/log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/GraphicBufferAlloc.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-GraphicBufferAlloc::GraphicBufferAlloc() {
-}
-
-GraphicBufferAlloc::~GraphicBufferAlloc() {
-}
-
-sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
-        uint32_t height, PixelFormat format, uint32_t usage,
-        std::string requestorName, status_t* error) {
-    sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
-            width, height, format, usage, std::move(requestorName)));
-    status_t err = graphicBuffer->initCheck();
-    *error = err;
-    if (err != 0 || graphicBuffer->handle == 0) {
-        if (err == NO_MEMORY) {
-            GraphicBuffer::dumpAllocationsToSystemLog();
-        }
-        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
-             "failed (%s), handle=%p",
-                width, height, strerror(-err), graphicBuffer->handle);
-        return 0;
-    }
-    return graphicBuffer;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/libs/gui/GraphicsEnv.cpp b/libs/gui/GraphicsEnv.cpp
deleted file mode 100644
index 68f0f98..0000000
--- a/libs/gui/GraphicsEnv.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 1
-#define LOG_TAG "GraphicsEnv"
-#include <gui/GraphicsEnv.h>
-
-#include <mutex>
-
-#include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
-
-namespace android {
-
-/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
-    static GraphicsEnv env;
-    return env;
-}
-
-void GraphicsEnv::setDriverPath(const std::string path) {
-    if (!mDriverPath.empty()) {
-        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
-                mDriverPath.c_str(), path.c_str());
-        return;
-    }
-    ALOGV("setting driver path to '%s'", path.c_str());
-    mDriverPath = path;
-}
-
-android_namespace_t* GraphicsEnv::getDriverNamespace() {
-    static std::once_flag once;
-    std::call_once(once, [this]() {
-        // TODO; In the next version of Android, all graphics drivers will be
-        // loaded into a custom namespace. To minimize risk for this release,
-        // only updated drivers use a custom namespace.
-        //
-        // Additionally, the custom namespace will be
-        // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a
-        // subset of the system.
-        if (mDriverPath.empty())
-            return;
-
-        char defaultPath[PATH_MAX];
-        android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath));
-        size_t defaultPathLen = strlen(defaultPath);
-
-        std::string path;
-        path.reserve(mDriverPath.size() + 1 + defaultPathLen);
-        path.append(mDriverPath);
-        path.push_back(':');
-        path.append(defaultPath, defaultPathLen);
-
-        mDriverNamespace = android_create_namespace(
-                "gfx driver",
-                nullptr,                    // ld_library_path
-                path.c_str(),               // default_library_path
-                ANDROID_NAMESPACE_TYPE_SHARED,
-                nullptr,                    // permitted_when_isolated_path
-                nullptr);                   // parent
-    });
-    return mDriverNamespace;
-}
-
-} // namespace android
-
-extern "C" android_namespace_t* android_getDriverNamespace() {
-    return android::GraphicsEnv::getInstance().getDriverNamespace();
-}
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 9a06011..85ac304 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -14,146 +14,86 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
 #include <gui/IConsumerListener.h>
+
 #include <gui/BufferItem.h>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
-enum {
-    ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
-    ON_BUFFER_RELEASED,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+    ON_FRAME_AVAILABLE,
+    ON_FRAME_REPLACED,
+    ON_BUFFERS_RELEASED,
     ON_SIDEBAND_STREAM_CHANGED,
-    GET_FRAME_TIMESTAMPS
+    LAST = ON_SIDEBAND_STREAM_CHANGED,
 };
 
-class BpConsumerListener : public BpInterface<IConsumerListener>
-{
+} // Anonymous namespace
+
+class BpConsumerListener : public SafeBpInterface<IConsumerListener> {
 public:
-    BpConsumerListener(const sp<IBinder>& impl)
-        : BpInterface<IConsumerListener>(impl) {
+    explicit BpConsumerListener(const sp<IBinder>& impl)
+          : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {}
+
+    ~BpConsumerListener() override;
+
+    void onDisconnect() override {
+        callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT);
     }
 
-    virtual ~BpConsumerListener();
-
-    virtual void onFrameAvailable(const BufferItem& item) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
-        data.write(item);
-        remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY);
+    void onFrameAvailable(const BufferItem& item) override {
+        callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE,
+                                                                        item);
     }
 
-    virtual void onBuffersReleased() {
-        Parcel data, reply;
-        data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
-        remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
+    void onFrameReplaced(const BufferItem& item) override {
+        callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED,
+                                                                       item);
     }
 
-    virtual void onSidebandStreamChanged() {
-        Parcel data, reply;
-        data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
-        remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+    void onBuffersReleased() override {
+        callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
     }
 
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const {
-        Parcel data, reply;
-        status_t result = data.writeInterfaceToken(
-                IConsumerListener::getInterfaceDescriptor());
-        if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to write token: %d", result);
-            return false;
-        }
-        result = data.writeUint64(frameNumber);
-        if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to write: %d", result);
-            return false;
-        }
-        result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to transact: %d", result);
-            return false;
-        }
-        bool found = false;
-        result = reply.readBool(&found);
-        if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to read: %d", result);
-            return false;
-        }
-        if (found) {
-            result = reply.read(*outTimestamps);
-            if (result != NO_ERROR) {
-                ALOGE("getFrameTimestamps failed to read timestamps: %d",
-                        result);
-                return false;
-            }
-        }
-        return found;
+    void onSidebandStreamChanged() override {
+        callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>(
+                Tag::ON_SIDEBAND_STREAM_CHANGED);
+    }
+
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
+                                  FrameEventHistoryDelta* /*outDelta*/) override {
+        LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
     }
 };
 
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpConsumerListener::~BpConsumerListener() {}
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpConsumerListener::~BpConsumerListener() = default;
+ConsumerListener::~ConsumerListener() = default;
 
 IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
 
-// ----------------------------------------------------------------------
-
-status_t BnConsumerListener::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case ON_FRAME_AVAILABLE: {
-            CHECK_INTERFACE(IConsumerListener, data, reply);
-            BufferItem item;
-            data.read(item);
-            onFrameAvailable(item);
-            return NO_ERROR; }
-        case ON_BUFFER_RELEASED: {
-            CHECK_INTERFACE(IConsumerListener, data, reply);
-            onBuffersReleased();
-            return NO_ERROR; }
-        case ON_SIDEBAND_STREAM_CHANGED: {
-            CHECK_INTERFACE(IConsumerListener, data, reply);
-            onSidebandStreamChanged();
-            return NO_ERROR; }
-        case GET_FRAME_TIMESTAMPS: {
-            CHECK_INTERFACE(IConsumerListener, data, reply);
-            uint64_t frameNumber = 0;
-            status_t result = data.readUint64(&frameNumber);
-            if (result != NO_ERROR) {
-                ALOGE("onTransact failed to read: %d", result);
-                return result;
-            }
-            FrameTimestamps timestamps;
-            bool found = getFrameTimestamps(frameNumber, &timestamps);
-            result = reply->writeBool(found);
-            if (result != NO_ERROR) {
-                ALOGE("onTransact failed to write: %d", result);
-                return result;
-            }
-            if (found) {
-                result = reply->write(timestamps);
-                if (result != NO_ERROR) {
-                    ALOGE("onTransact failed to write timestamps: %d", result);
-                    return result;
-                }
-            }
-            return NO_ERROR;
-        }
+status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                        uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
     }
-    return BBinder::onTransact(code, data, reply, flags);
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_DISCONNECT:
+            return callLocalAsync(data, reply, &IConsumerListener::onDisconnect);
+        case Tag::ON_FRAME_AVAILABLE:
+            return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
+        case Tag::ON_FRAME_REPLACED:
+            return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
+        case Tag::ON_BUFFERS_RELEASED:
+            return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
+        case Tag::ON_SIDEBAND_STREAM_CHANGED:
+            return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged);
+    }
 }
 
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index 9890f44..c0e246f 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -14,91 +14,67 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
 #include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
+
+#include <private/gui/BitTube.h>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
-enum {
-    GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
-    REQUEST_NEXT_VSYNC
+    REQUEST_NEXT_VSYNC,
+    LAST = REQUEST_NEXT_VSYNC,
 };
 
-class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
-{
+} // Anonymous namespace
+
+class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
 public:
-    BpDisplayEventConnection(const sp<IBinder>& impl)
-        : BpInterface<IDisplayEventConnection>(impl)
-    {
+    explicit BpDisplayEventConnection(const sp<IBinder>& impl)
+          : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
+
+    ~BpDisplayEventConnection() override;
+
+    status_t stealReceiveChannel(gui::BitTube* outChannel) override {
+        return callRemote<decltype(
+                &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
+                                                                outChannel);
     }
 
-    virtual ~BpDisplayEventConnection();
-
-    virtual sp<BitTube> getDataChannel() const
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
-        remote()->transact(GET_DATA_CHANNEL, data, &reply);
-        return new BitTube(reply);
+    status_t setVsyncRate(uint32_t count) override {
+        return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
+                                                                            count);
     }
 
-    virtual void setVsyncRate(uint32_t count) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
-        data.writeUint32(count);
-        remote()->transact(SET_VSYNC_RATE, data, &reply);
-    }
-
-    virtual void requestNextVsync() {
-        Parcel data, reply;
-        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
-        remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY);
+    void requestNextVsync() override {
+        callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
+                Tag::REQUEST_NEXT_VSYNC);
     }
 };
 
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpDisplayEventConnection::~BpDisplayEventConnection() = default;
 
 IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
 
-// ----------------------------------------------------------------------------
-
-status_t BnDisplayEventConnection::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case GET_DATA_CHANNEL: {
-            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
-            sp<BitTube> channel(getDataChannel());
-            channel->writeToParcel(reply);
-            return NO_ERROR;
-        }
-        case SET_VSYNC_RATE: {
-            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
-            setVsyncRate(data.readUint32());
-            return NO_ERROR;
-        }
-        case REQUEST_NEXT_VSYNC: {
-            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
-            requestNextVsync();
-            return NO_ERROR;
-        }
+status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                              uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
     }
-    return BBinder::onTransact(code, data, reply, flags);
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::STEAL_RECEIVE_CHANNEL:
+            return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
+        case Tag::SET_VSYNC_RATE:
+            return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
+        case Tag::REQUEST_NEXT_VSYNC:
+            return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+    }
 }
 
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
deleted file mode 100644
index 7b3b7c1..0000000
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// tag as surfaceflinger
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/IGraphicBufferAlloc.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-enum {
-    CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
-{
-public:
-    BpGraphicBufferAlloc(const sp<IBinder>& impl)
-        : BpInterface<IGraphicBufferAlloc>(impl)
-    {
-    }
-
-    virtual ~BpGraphicBufferAlloc();
-
-    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
-            std::string requestorName, status_t* error) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
-        data.writeUint32(width);
-        data.writeUint32(height);
-        data.writeInt32(static_cast<int32_t>(format));
-        data.writeUint32(usage);
-        if (requestorName.empty()) {
-            requestorName += "[PID ";
-            requestorName += std::to_string(getpid());
-            requestorName += ']';
-        }
-        data.writeUtf8AsUtf16(requestorName);
-        remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
-        sp<GraphicBuffer> graphicBuffer;
-        status_t result = reply.readInt32();
-        if (result == NO_ERROR) {
-            graphicBuffer = new GraphicBuffer();
-            result = reply.read(*graphicBuffer);
-            if (result != NO_ERROR) {
-                graphicBuffer.clear();
-            }
-            // reply.readStrongBinder();
-            // here we don't even have to read the BufferReference from
-            // the parcel, it'll die with the parcel.
-        }
-        *error = result;
-        return graphicBuffer;
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferAlloc::~BpGraphicBufferAlloc() {}
-
-IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");
-
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferAlloc::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    // codes that don't require permission check
-
-    // BufferReference just keeps a strong reference to a GraphicBuffer until it
-    // is destroyed (that is, until no local or remote process have a reference
-    // to it).
-    class BufferReference : public BBinder {
-        sp<GraphicBuffer> mBuffer;
-    public:
-        BufferReference(const sp<GraphicBuffer>& buffer) : mBuffer(buffer) {}
-    };
-
-
-    switch (code) {
-        case CREATE_GRAPHIC_BUFFER: {
-            CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
-            uint32_t width = data.readUint32();
-            uint32_t height = data.readUint32();
-            PixelFormat format = static_cast<PixelFormat>(data.readInt32());
-            uint32_t usage = data.readUint32();
-            status_t error = NO_ERROR;
-            std::string requestorName;
-            data.readUtf8FromUtf16(&requestorName);
-            sp<GraphicBuffer> result = createGraphicBuffer(width, height,
-                    format, usage, requestorName, &error);
-            reply->writeInt32(error);
-            if (result != 0) {
-                reply->write(*result);
-                // We add a BufferReference to this parcel to make sure the
-                // buffer stays alive until the GraphicBuffer object on
-                // the other side has been created.
-                // This is needed so that the buffer handle can be
-                // registered before the buffer is destroyed on implementations
-                // that do not use file-descriptors to track their buffers.
-                reply->writeStrongBinder( new BufferReference(result) );
-            }
-            return NO_ERROR;
-        }
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-}; // namespace android
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index c8eff00..a573bee 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -14,27 +14,24 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/NativeHandle.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
+#include <gui/IGraphicBufferConsumer.h>
 
 #include <gui/BufferItem.h>
 #include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferConsumer.h>
 
-#include <ui/GraphicBuffer.h>
+#include <binder/Parcel.h>
+
 #include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
 
-#include <system/window.h>
+#include <utils/NativeHandle.h>
+#include <utils/String8.h>
 
 namespace android {
 
-enum {
+namespace { // Anonymous namespace
+
+enum class Tag : uint32_t {
     ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
     DETACH_BUFFER,
     ATTACH_BUFFER,
@@ -49,443 +46,185 @@
     SET_DEFAULT_BUFFER_FORMAT,
     SET_DEFAULT_BUFFER_DATA_SPACE,
     SET_CONSUMER_USAGE_BITS,
+    SET_CONSUMER_IS_PROTECTED,
     SET_TRANSFORM_HINT,
     GET_SIDEBAND_STREAM,
     GET_OCCUPANCY_HISTORY,
     DISCARD_FREE_BUFFERS,
-    DUMP,
+    DUMP_STATE,
+    LAST = DUMP_STATE,
 };
 
+} // Anonymous namespace
 
-class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer>
-{
+class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> {
 public:
-    BpGraphicBufferConsumer(const sp<IBinder>& impl)
-        : BpInterface<IGraphicBufferConsumer>(impl)
-    {
+    explicit BpGraphicBufferConsumer(const sp<IBinder>& impl)
+          : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {}
+
+    ~BpGraphicBufferConsumer() override;
+
+    status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
+                           uint64_t maxFrameNumber) override {
+        using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer);
+        return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber);
     }
 
-    virtual ~BpGraphicBufferConsumer();
-
-    virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen,
-            uint64_t maxFrameNumber) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt64(presentWhen);
-        data.writeUint64(maxFrameNumber);
-        status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        result = reply.read(*buffer);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t detachBuffer(int slot) override {
+        using Signature = decltype(&IGraphicBufferConsumer::detachBuffer);
+        return callRemote<Signature>(Tag::DETACH_BUFFER, slot);
     }
 
-    virtual status_t detachBuffer(int slot) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(slot);
-        status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        result = reply.readInt32();
-        return result;
+    status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override {
+        using Signature = decltype(&IGraphicBufferConsumer::attachBuffer);
+        return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer);
     }
 
-    virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.write(*buffer.get());
-        status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        *slot = reply.readInt32();
-        result = reply.readInt32();
-        return result;
+    status_t releaseBuffer(int buf, uint64_t frameNumber,
+                           EGLDisplay display __attribute__((unused)),
+                           EGLSyncKHR fence __attribute__((unused)),
+                           const sp<Fence>& releaseFence) override {
+        return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
     }
 
-    virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
-            EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
-            const sp<Fence>& releaseFence) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(buf);
-        data.writeInt64(static_cast<int64_t>(frameNumber));
-        data.write(*releaseFence);
-        status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override {
+        using Signature = decltype(&IGraphicBufferConsumer::consumerConnect);
+        return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp);
     }
 
-    virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeStrongBinder(IInterface::asBinder(consumer));
-        data.writeInt32(controlledByApp);
-        status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t consumerDisconnect() override {
+        return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>(
+                Tag::CONSUMER_DISCONNECT);
     }
 
-    virtual status_t consumerDisconnect() {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t getReleasedBuffers(uint64_t* slotMask) override {
+        using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers);
+        return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
     }
 
-    virtual status_t getReleasedBuffers(uint64_t* slotMask) {
-        Parcel data, reply;
-        if (slotMask == NULL) {
-            ALOGE("getReleasedBuffers: slotMask must not be NULL");
-            return BAD_VALUE;
-        }
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        *slotMask = static_cast<uint64_t>(reply.readInt64());
-        return reply.readInt32();
+    status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
+        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
     }
 
-    virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeUint32(width);
-        data.writeUint32(height);
-        status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setMaxBufferCount(int bufferCount) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
+        return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
     }
 
-    virtual status_t setMaxBufferCount(int bufferCount) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(bufferCount);
-        status_t result = remote()->transact(SET_MAX_BUFFER_COUNT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+        return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers);
     }
 
-    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(maxAcquiredBuffers);
-        status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setConsumerName(const String8& name) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setConsumerName);
+        return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name);
     }
 
-    virtual void setConsumerName(const String8& name) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeString8(name);
-        remote()->transact(SET_CONSUMER_NAME, data, &reply);
+    status_t setDefaultBufferFormat(PixelFormat defaultFormat) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat);
+        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat);
     }
 
-    virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(static_cast<int32_t>(defaultFormat));
-        status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace);
+        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace);
     }
 
-    virtual status_t setDefaultBufferDataSpace(
-            android_dataspace defaultDataSpace) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeInt32(static_cast<int32_t>(defaultDataSpace));
-        status_t result = remote()->transact(SET_DEFAULT_BUFFER_DATA_SPACE,
-                data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setConsumerUsageBits(uint32_t usage) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits);
+        return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage);
     }
 
-    virtual status_t setConsumerUsageBits(uint32_t usage) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeUint32(usage);
-        status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setConsumerIsProtected(bool isProtected) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected);
+        return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected);
     }
 
-    virtual status_t setTransformHint(uint32_t hint) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeUint32(hint);
-        status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        return reply.readInt32();
+    status_t setTransformHint(uint32_t hint) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setTransformHint);
+        return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint);
     }
 
-    virtual sp<NativeHandle> getSidebandStream() const {
-        Parcel data, reply;
-        status_t err;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
-            return NULL;
-        }
-        sp<NativeHandle> stream;
-        if (reply.readInt32()) {
-            stream = NativeHandle::create(reply.readNativeHandle(), true);
-        }
-        return stream;
+    status_t getSidebandStream(sp<NativeHandle>* outStream) const override {
+        using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream);
+        return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream);
     }
 
-    virtual status_t getOccupancyHistory(bool forceFlush,
-            std::vector<OccupancyTracker::Segment>* outHistory) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        status_t error = data.writeBool(forceFlush);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
-                &reply);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        error = reply.readParcelableVector(outHistory);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        status_t result = NO_ERROR;
-        error = reply.readInt32(&result);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        return result;
+    status_t getOccupancyHistory(bool forceFlush,
+                                 std::vector<OccupancyTracker::Segment>* outHistory) override {
+        using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory);
+        return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory);
     }
 
-    virtual status_t discardFreeBuffers() {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        int32_t result = NO_ERROR;
-        error = reply.readInt32(&result);
-        if (error != NO_ERROR) {
-            return error;
-        }
-        return result;
+    status_t discardFreeBuffers() override {
+        return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>(
+                Tag::DISCARD_FREE_BUFFERS);
     }
 
-    virtual void dump(String8& result, const char* prefix) const {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
-        data.writeString8(result);
-        data.writeString8(String8(prefix ? prefix : ""));
-        remote()->transact(DUMP, data, &reply);
-        reply.readString8();
+    status_t dumpState(const String8& prefix, String8* outResult) const override {
+        using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+        return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
     }
 };
 
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferConsumer::~BpGraphicBufferConsumer() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit
+// (see clang warning -Wweak-vtables)
+BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default;
 
 IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
 
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferConsumer::onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case ACQUIRE_BUFFER: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            BufferItem item;
-            int64_t presentWhen = data.readInt64();
-            uint64_t maxFrameNumber = data.readUint64();
-            status_t result = acquireBuffer(&item, presentWhen, maxFrameNumber);
-            status_t err = reply->write(item);
-            if (err) return err;
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case DETACH_BUFFER: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            int slot = data.readInt32();
-            int result = detachBuffer(slot);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case ATTACH_BUFFER: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            sp<GraphicBuffer> buffer = new GraphicBuffer();
-            data.read(*buffer.get());
-            int slot = -1;
-            int result = attachBuffer(&slot, buffer);
-            reply->writeInt32(slot);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case RELEASE_BUFFER: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            int buf = data.readInt32();
-            uint64_t frameNumber = static_cast<uint64_t>(data.readInt64());
-            sp<Fence> releaseFence = new Fence();
-            status_t err = data.read(*releaseFence);
-            if (err) return err;
-            status_t result = releaseBuffer(buf, frameNumber,
-                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case CONSUMER_CONNECT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
-            bool controlledByApp = data.readInt32();
-            status_t result = consumerConnect(consumer, controlledByApp);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case CONSUMER_DISCONNECT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            status_t result = consumerDisconnect();
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case GET_RELEASED_BUFFERS: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            uint64_t slotMask = 0;
-            status_t result = getReleasedBuffers(&slotMask);
-            reply->writeInt64(static_cast<int64_t>(slotMask));
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_DEFAULT_BUFFER_SIZE: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            uint32_t width = data.readUint32();
-            uint32_t height = data.readUint32();
-            status_t result = setDefaultBufferSize(width, height);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_MAX_BUFFER_COUNT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            int bufferCount = data.readInt32();
-            status_t result = setMaxBufferCount(bufferCount);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_MAX_ACQUIRED_BUFFER_COUNT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            int maxAcquiredBuffers = data.readInt32();
-            status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_CONSUMER_NAME: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            setConsumerName( data.readString8() );
-            return NO_ERROR;
-        }
-        case SET_DEFAULT_BUFFER_FORMAT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            PixelFormat defaultFormat = static_cast<PixelFormat>(data.readInt32());
-            status_t result = setDefaultBufferFormat(defaultFormat);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_DEFAULT_BUFFER_DATA_SPACE: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            android_dataspace defaultDataSpace =
-                    static_cast<android_dataspace>(data.readInt32());
-            status_t result = setDefaultBufferDataSpace(defaultDataSpace);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_CONSUMER_USAGE_BITS: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            uint32_t usage = data.readUint32();
-            status_t result = setConsumerUsageBits(usage);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_TRANSFORM_HINT: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            uint32_t hint = data.readUint32();
-            status_t result = setTransformHint(hint);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case GET_SIDEBAND_STREAM: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            sp<NativeHandle> stream = getSidebandStream();
-            reply->writeInt32(static_cast<int32_t>(stream != NULL));
-            if (stream != NULL) {
-                reply->writeNativeHandle(stream->handle());
-            }
-            return NO_ERROR;
-        }
-        case GET_OCCUPANCY_HISTORY: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            bool forceFlush = false;
-            status_t error = data.readBool(&forceFlush);
-            if (error != NO_ERROR) {
-                return error;
-            }
-            std::vector<OccupancyTracker::Segment> history;
-            status_t result = getOccupancyHistory(forceFlush, &history);
-            error = reply->writeParcelableVector(history);
-            if (error != NO_ERROR) {
-                return error;
-            }
-            error = reply->writeInt32(result);
-            if (error != NO_ERROR) {
-                return error;
-            }
-            return NO_ERROR;
-        }
-        case DISCARD_FREE_BUFFERS: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            status_t result = discardFreeBuffers();
-            status_t error = reply->writeInt32(result);
-            return error;
-        }
-        case DUMP: {
-            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
-            String8 result = data.readString8();
-            String8 prefix = data.readString8();
-            static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix);
-            reply->writeString8(result);
-            return NO_ERROR;
+status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                             uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ACQUIRE_BUFFER:
+            return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer);
+        case Tag::DETACH_BUFFER:
+            return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer);
+        case Tag::ATTACH_BUFFER:
+            return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer);
+        case Tag::RELEASE_BUFFER:
+            return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper);
+        case Tag::CONSUMER_CONNECT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect);
+        case Tag::CONSUMER_DISCONNECT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect);
+        case Tag::GET_RELEASED_BUFFERS:
+            return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers);
+        case Tag::SET_DEFAULT_BUFFER_SIZE:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize);
+        case Tag::SET_MAX_BUFFER_COUNT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount);
+        case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+        case Tag::SET_CONSUMER_NAME:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName);
+        case Tag::SET_DEFAULT_BUFFER_FORMAT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat);
+        case Tag::SET_DEFAULT_BUFFER_DATA_SPACE:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace);
+        case Tag::SET_CONSUMER_USAGE_BITS:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits);
+        case Tag::SET_CONSUMER_IS_PROTECTED:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected);
+        case Tag::SET_TRANSFORM_HINT:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint);
+        case Tag::GET_SIDEBAND_STREAM:
+            return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream);
+        case Tag::GET_OCCUPANCY_HISTORY:
+            return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
+        case Tag::DISCARD_FREE_BUFFERS:
+            return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
+        case Tag::DUMP_STATE: {
+            using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+            return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
         }
     }
-    return BBinder::onTransact(code, data, reply, flags);
 }
 
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 1a08130..bca645f 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -20,6 +20,7 @@
 #include <utils/Errors.h>
 #include <utils/NativeHandle.h>
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Vector.h>
 
@@ -30,9 +31,14 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+        H2BGraphicBufferProducer;
+
 enum {
     REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
     DEQUEUE_BUFFER,
@@ -62,7 +68,7 @@
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
 {
 public:
-    BpGraphicBufferProducer(const sp<IBinder>& impl)
+    explicit BpGraphicBufferProducer(const sp<IBinder>& impl)
         : BpInterface<IGraphicBufferProducer>(impl)
     {
     }
@@ -119,24 +125,35 @@
     }
 
     virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage) {
+            uint32_t height, PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) {
         Parcel data, reply;
+        bool getFrameTimestamps = (outTimestamps != nullptr);
+
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
         data.writeUint32(width);
         data.writeUint32(height);
         data.writeInt32(static_cast<int32_t>(format));
         data.writeUint32(usage);
+        data.writeBool(getFrameTimestamps);
+
         status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
         if (result != NO_ERROR) {
             return result;
         }
+
         *buf = reply.readInt32();
-        bool nonNull = reply.readInt32();
-        if (nonNull) {
-            *fence = new Fence();
-            result = reply.read(**fence);
+        *fence = new Fence();
+        result = reply.read(**fence);
+        if (result != NO_ERROR) {
+            fence->clear();
+            return result;
+        }
+        if (getFrameTimestamps) {
+            result = reply.read(*outTimestamps);
             if (result != NO_ERROR) {
-                fence->clear();
+                ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",
+                        result);
                 return result;
             }
         }
@@ -220,14 +237,21 @@
     virtual status_t queueBuffer(int buf,
             const QueueBufferInput& input, QueueBufferOutput* output) {
         Parcel data, reply;
+
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
         data.writeInt32(buf);
         data.write(input);
+
         status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
         if (result != NO_ERROR) {
             return result;
         }
-        memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+
+        result = reply.read(*output);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
         result = reply.readInt32();
         return result;
     }
@@ -274,7 +298,7 @@
         if (result != NO_ERROR) {
             return result;
         }
-        memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+        reply.read(*output);
         result = reply.readInt32();
         return result;
     }
@@ -431,40 +455,24 @@
         return result;
     }
 
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-                FrameTimestamps* outTimestamps) const {
+    virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(
                 IGraphicBufferProducer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to write token: %d", result);
-            return false;
-        }
-        result = data.writeUint64(frameNumber);
-        if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to write: %d", result);
-            return false;
+            ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result);
+            return;
         }
         result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
         if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to transact: %d", result);
-            return false;
+            ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result);
+            return;
         }
-        bool found = false;
-        result = reply.readBool(&found);
+        result = reply.read(*outDelta);
         if (result != NO_ERROR) {
-            ALOGE("getFrameTimestamps failed to read: %d", result);
-            return false;
+            ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d",
+                    result);
         }
-        if (found) {
-            result = reply.read(*outTimestamps);
-            if (result != NO_ERROR) {
-                ALOGE("getFrameTimestamps failed to read timestamps: %d",
-                        result);
-                return false;
-            }
-        }
-        return found;
     }
 
     virtual status_t getUniqueId(uint64_t* outId) const {
@@ -491,7 +499,123 @@
 // translation unit (see clang warning -Wweak-vtables)
 BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
 
-IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
+class HpGraphicBufferProducer : public HpInterface<
+        BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+public:
+    HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
+
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+        return mBase->requestBuffer(slot, buf);
+    }
+
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+        return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+    }
+
+    status_t setAsyncMode(bool async) override {
+        return mBase->setAsyncMode(async);
+    }
+
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override {
+        return mBase->dequeueBuffer(
+                slot, fence, w, h, format, usage, outTimestamps);
+    }
+
+    status_t detachBuffer(int slot) override {
+        return mBase->detachBuffer(slot);
+    }
+
+    status_t detachNextBuffer(
+            sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+        return mBase->detachNextBuffer(outBuffer, outFence);
+    }
+
+    status_t attachBuffer(
+            int* outSlot, const sp<GraphicBuffer>& buffer) override {
+        return mBase->attachBuffer(outSlot, buffer);
+    }
+
+    status_t queueBuffer(
+            int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override {
+        return mBase->queueBuffer(slot, input, output);
+    }
+
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+        return mBase->cancelBuffer(slot, fence);
+    }
+
+    int query(int what, int* value) override {
+        return mBase->query(what, value);
+    }
+
+    status_t connect(
+            const sp<IProducerListener>& listener,
+            int api, bool producerControlledByApp,
+            QueueBufferOutput* output) override {
+        return mBase->connect(listener, api, producerControlledByApp, output);
+    }
+
+    status_t disconnect(
+            int api, DisconnectMode mode = DisconnectMode::Api) override {
+        return mBase->disconnect(api, mode);
+    }
+
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+        return mBase->setSidebandStream(stream);
+    }
+
+    void allocateBuffers(uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t usage) override {
+        return mBase->allocateBuffers(width, height, format, usage);
+    }
+
+    status_t allowAllocation(bool allow) override {
+        return mBase->allowAllocation(allow);
+    }
+
+    status_t setGenerationNumber(uint32_t generationNumber) override {
+        return mBase->setGenerationNumber(generationNumber);
+    }
+
+    String8 getConsumerName() const override {
+        return mBase->getConsumerName();
+    }
+
+    status_t setSharedBufferMode(bool sharedBufferMode) override {
+        return mBase->setSharedBufferMode(sharedBufferMode);
+    }
+
+    status_t setAutoRefresh(bool autoRefresh) override {
+        return mBase->setAutoRefresh(autoRefresh);
+    }
+
+    status_t setDequeueTimeout(nsecs_t timeout) override {
+        return mBase->setDequeueTimeout(timeout);
+    }
+
+    status_t getLastQueuedBuffer(
+            sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence,
+            float outTransformMatrix[16]) override {
+        return mBase->getLastQueuedBuffer(
+                outBuffer, outFence, outTransformMatrix);
+    }
+
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
+        return mBase->getFrameTimestamps(outDelta);
+    }
+
+    status_t getUniqueId(uint64_t* outId) const override {
+        return mBase->getUniqueId(outId);
+    }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+        "android.gui.IGraphicBufferProducer");
 
 // ----------------------------------------------------------------------
 
@@ -531,14 +655,18 @@
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
             uint32_t usage = data.readUint32();
+            bool getTimestamps = data.readBool();
+
             int buf = 0;
-            sp<Fence> fence;
+            sp<Fence> fence = Fence::NO_FENCE;
+            FrameEventHistoryDelta frameTimestamps;
             int result = dequeueBuffer(&buf, &fence, width, height, format,
-                    usage);
+                    usage, getTimestamps ? &frameTimestamps : nullptr);
+
             reply->writeInt32(buf);
-            reply->writeInt32(fence != NULL);
-            if (fence != NULL) {
-                reply->write(*fence);
+            reply->write(*fence);
+            if (getTimestamps) {
+                reply->write(frameTimestamps);
             }
             reply->writeInt32(result);
             return NO_ERROR;
@@ -582,14 +710,14 @@
         }
         case QUEUE_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+
             int buf = data.readInt32();
             QueueBufferInput input(data);
-            QueueBufferOutput* const output =
-                    reinterpret_cast<QueueBufferOutput *>(
-                            reply->writeInplace(sizeof(QueueBufferOutput)));
-            memset(output, 0, sizeof(QueueBufferOutput));
-            status_t result = queueBuffer(buf, input, output);
+            QueueBufferOutput output;
+            status_t result = queueBuffer(buf, input, &output);
+            reply->write(output);
             reply->writeInt32(result);
+
             return NO_ERROR;
         }
         case CANCEL_BUFFER: {
@@ -620,11 +748,9 @@
             }
             int api = data.readInt32();
             bool producerControlledByApp = data.readInt32();
-            QueueBufferOutput* const output =
-                    reinterpret_cast<QueueBufferOutput *>(
-                            reply->writeInplace(sizeof(QueueBufferOutput)));
-            memset(output, 0, sizeof(QueueBufferOutput));
-            status_t res = connect(listener, api, producerControlledByApp, output);
+            QueueBufferOutput output;
+            status_t res = connect(listener, api, producerControlledByApp, &output);
+            reply->write(output);
             reply->writeInt32(res);
             return NO_ERROR;
         }
@@ -727,26 +853,14 @@
         }
         case GET_FRAME_TIMESTAMPS: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
-            uint64_t frameNumber = 0;
-            status_t result = data.readUint64(&frameNumber);
+            FrameEventHistoryDelta frameTimestamps;
+            getFrameTimestamps(&frameTimestamps);
+            status_t result = reply->write(frameTimestamps);
             if (result != NO_ERROR) {
-                ALOGE("onTransact failed to read: %d", result);
+                ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d",
+                        result);
                 return result;
             }
-            FrameTimestamps timestamps;
-            bool found = getFrameTimestamps(frameNumber, &timestamps);
-            result = reply->writeBool(found);
-            if (result != NO_ERROR) {
-                ALOGE("onTransact failed to write: %d", result);
-                return result;
-            }
-            if (found) {
-                result = reply->write(timestamps);
-                if (result != NO_ERROR) {
-                    ALOGE("onTransact failed to write timestamps: %d", result);
-                    return result;
-                }
-            }
             return NO_ERROR;
         }
         case GET_UNIQUE_ID: {
@@ -773,16 +887,21 @@
     parcel.read(*this);
 }
 
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+    return sizeof(timestamp) +
+            sizeof(isAutoTimestamp) +
+            sizeof(dataSpace) +
+            sizeof(crop) +
+            sizeof(scalingMode) +
+            sizeof(transform) +
+            sizeof(stickyTransform) +
+            sizeof(getFrameTimestamps);
+}
+
 size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
-    return sizeof(timestamp)
-         + sizeof(isAutoTimestamp)
-         + sizeof(dataSpace)
-         + sizeof(crop)
-         + sizeof(scalingMode)
-         + sizeof(transform)
-         + sizeof(stickyTransform)
-         + fence->getFlattenedSize()
-         + surfaceDamage.getFlattenedSize();
+    return minFlattenedSize() +
+            fence->getFlattenedSize() +
+            surfaceDamage.getFlattenedSize();
 }
 
 size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -795,6 +914,7 @@
     if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
+
     FlattenableUtils::write(buffer, size, timestamp);
     FlattenableUtils::write(buffer, size, isAutoTimestamp);
     FlattenableUtils::write(buffer, size, dataSpace);
@@ -802,6 +922,8 @@
     FlattenableUtils::write(buffer, size, scalingMode);
     FlattenableUtils::write(buffer, size, transform);
     FlattenableUtils::write(buffer, size, stickyTransform);
+    FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
     status_t result = fence->flatten(buffer, size, fds, count);
     if (result != NO_ERROR) {
         return result;
@@ -812,16 +934,7 @@
 status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
         void const*& buffer, size_t& size, int const*& fds, size_t& count)
 {
-    size_t minNeeded =
-              sizeof(timestamp)
-            + sizeof(isAutoTimestamp)
-            + sizeof(dataSpace)
-            + sizeof(crop)
-            + sizeof(scalingMode)
-            + sizeof(transform)
-            + sizeof(stickyTransform);
-
-    if (size < minNeeded) {
+    if (size < minFlattenedSize()) {
         return NO_MEMORY;
     }
 
@@ -832,6 +945,7 @@
     FlattenableUtils::read(buffer, size, scalingMode);
     FlattenableUtils::read(buffer, size, transform);
     FlattenableUtils::read(buffer, size, stickyTransform);
+    FlattenableUtils::read(buffer, size, getFrameTimestamps);
 
     fence = new Fence();
     status_t result = fence->unflatten(buffer, size, fds, count);
@@ -841,4 +955,56 @@
     return surfaceDamage.unflatten(buffer, size);
 }
 
+// ----------------------------------------------------------------------------
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+    return sizeof(width) +
+            sizeof(height) +
+            sizeof(transformHint) +
+            sizeof(numPendingBuffers) +
+            sizeof(nextFrameNumber) +
+            sizeof(bufferReplaced);
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+    return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, width);
+    FlattenableUtils::write(buffer, size, height);
+    FlattenableUtils::write(buffer, size, transformHint);
+    FlattenableUtils::write(buffer, size, numPendingBuffers);
+    FlattenableUtils::write(buffer, size, nextFrameNumber);
+    FlattenableUtils::write(buffer, size, bufferReplaced);
+
+    return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, width);
+    FlattenableUtils::read(buffer, size, height);
+    FlattenableUtils::read(buffer, size, transformHint);
+    FlattenableUtils::read(buffer, size, numPendingBuffers);
+    FlattenableUtils::read(buffer, size, nextFrameNumber);
+    FlattenableUtils::read(buffer, size, bufferReplaced);
+
+    return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
 }; // namespace android
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index da54ce1..62abfa8 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -28,7 +28,7 @@
 class BpProducerListener : public BpInterface<IProducerListener>
 {
 public:
-    BpProducerListener(const sp<IBinder>& impl)
+    explicit BpProducerListener(const sp<IBinder>& impl)
         : BpInterface<IProducerListener>(impl) {}
 
     virtual ~BpProducerListener();
@@ -78,6 +78,10 @@
     return BBinder::onTransact(code, data, reply, flags);
 }
 
+ProducerListener::~ProducerListener() = default;
+
+DummyProducerListener::~DummyProducerListener() = default;
+
 bool BnProducerListener::needsReleaseNotify() {
     return true;
 }
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
deleted file mode 100644
index dc7a35c..0000000
--- a/libs/gui/ISensorEventConnection.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/ISensorEventConnection.h>
-#include <gui/BitTube.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
-    GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
-    ENABLE_DISABLE,
-    SET_EVENT_RATE,
-    FLUSH_SENSOR
-};
-
-class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
-{
-public:
-    BpSensorEventConnection(const sp<IBinder>& impl)
-        : BpInterface<ISensorEventConnection>(impl)
-    {
-    }
-
-    virtual ~BpSensorEventConnection();
-
-    virtual sp<BitTube> getSensorChannel() const
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
-        return new BitTube(reply);
-    }
-
-    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
-                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        data.writeInt32(handle);
-        data.writeInt32(enabled);
-        data.writeInt64(samplingPeriodNs);
-        data.writeInt64(maxBatchReportLatencyNs);
-        data.writeInt32(reservedFlags);
-        remote()->transact(ENABLE_DISABLE, data, &reply);
-        return reply.readInt32();
-    }
-
-    virtual status_t setEventRate(int handle, nsecs_t ns)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        data.writeInt32(handle);
-        data.writeInt64(ns);
-        remote()->transact(SET_EVENT_RATE, data, &reply);
-        return reply.readInt32();
-    }
-
-    virtual status_t flush() {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        remote()->transact(FLUSH_SENSOR, data, &reply);
-        return reply.readInt32();
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorEventConnection::~BpSensorEventConnection() {}
-
-IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
-
-// ----------------------------------------------------------------------------
-
-status_t BnSensorEventConnection::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case GET_SENSOR_CHANNEL: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            sp<BitTube> channel(getSensorChannel());
-            channel->writeToParcel(reply);
-            return NO_ERROR;
-        }
-        case ENABLE_DISABLE: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            int handle = data.readInt32();
-            int enabled = data.readInt32();
-            nsecs_t samplingPeriodNs = data.readInt64();
-            nsecs_t maxBatchReportLatencyNs = data.readInt64();
-            int reservedFlags = data.readInt32();
-            status_t result = enableDisable(handle, enabled, samplingPeriodNs,
-                                            maxBatchReportLatencyNs, reservedFlags);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_EVENT_RATE: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            int handle = data.readInt32();
-            nsecs_t ns = data.readInt64();
-            status_t result = setEventRate(handle, ns);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case FLUSH_SENSOR: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            status_t result = flush();
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-    }
-    return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
deleted file mode 100644
index 3a4c7e4..0000000
--- a/libs/gui/ISensorServer.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/Sensor.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
-    GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
-    CREATE_SENSOR_EVENT_CONNECTION,
-    ENABLE_DATA_INJECTION,
-    GET_DYNAMIC_SENSOR_LIST,
-};
-
-class BpSensorServer : public BpInterface<ISensorServer>
-{
-public:
-    BpSensorServer(const sp<IBinder>& impl)
-        : BpInterface<ISensorServer>(impl)
-    {
-    }
-
-    virtual ~BpSensorServer();
-
-    virtual Vector<Sensor> getSensorList(const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString16(opPackageName);
-        remote()->transact(GET_SENSOR_LIST, data, &reply);
-        Sensor s;
-        Vector<Sensor> v;
-        uint32_t n = reply.readUint32();
-        v.setCapacity(n);
-        while (n--) {
-            reply.read(s);
-            v.add(s);
-        }
-        return v;
-    }
-
-    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString16(opPackageName);
-        remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply);
-        Sensor s;
-        Vector<Sensor> v;
-        uint32_t n = reply.readUint32();
-        v.setCapacity(n);
-        while (n--) {
-            reply.read(s);
-            v.add(s);
-        }
-        return v;
-    }
-
-    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
-             int mode, const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString8(packageName);
-        data.writeInt32(mode);
-        data.writeString16(opPackageName);
-        remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
-        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
-    }
-
-    virtual int isDataInjectionEnabled() {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
-        return reply.readInt32();
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorServer::~BpSensorServer() {}
-
-IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
-
-// ----------------------------------------------------------------------
-
-status_t BnSensorServer::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case GET_SENSOR_LIST: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            const String16& opPackageName = data.readString16();
-            Vector<Sensor> v(getSensorList(opPackageName));
-            size_t n = v.size();
-            reply->writeUint32(static_cast<uint32_t>(n));
-            for (size_t i = 0; i < n; i++) {
-                reply->write(v[i]);
-            }
-            return NO_ERROR;
-        }
-        case CREATE_SENSOR_EVENT_CONNECTION: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            String8 packageName = data.readString8();
-            int32_t mode = data.readInt32();
-            const String16& opPackageName = data.readString16();
-            sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
-                    opPackageName));
-            reply->writeStrongBinder(IInterface::asBinder(connection));
-            return NO_ERROR;
-        }
-        case ENABLE_DATA_INJECTION: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            int32_t ret = isDataInjectionEnabled();
-            reply->writeInt32(static_cast<int32_t>(ret));
-            return NO_ERROR;
-        }
-        case GET_DYNAMIC_SENSOR_LIST: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            const String16& opPackageName = data.readString16();
-            Vector<Sensor> v(getDynamicSensorList(opPackageName));
-            size_t n = v.size();
-            reply->writeUint32(static_cast<uint32_t>(n));
-            for (size_t i = 0; i < n; i++) {
-                reply->write(v[i]);
-            }
-            return NO_ERROR;
-        }
-    }
-    return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index f0b0ada..0a0d112 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -21,14 +21,13 @@
 #include <sys/types.h>
 
 #include <binder/Parcel.h>
-#include <binder/IMemory.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
-#include <gui/BitTube.h>
 #include <gui/IDisplayEventConnection.h>
-#include <gui/ISurfaceComposer.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/ISurfaceComposerClient.h>
 
 #include <private/gui/LayerState.h>
 
@@ -44,12 +43,10 @@
 
 namespace android {
 
-class IDisplayEventConnection;
-
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
 {
 public:
-    BpSurfaceComposer(const sp<IBinder>& impl)
+    explicit BpSurfaceComposer(const sp<IBinder>& impl)
         : BpInterface<ISurfaceComposer>(impl)
     {
     }
@@ -64,12 +61,14 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
-    virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
+    virtual sp<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& parent)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply);
-        return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder());
+        data.writeStrongBinder(IInterface::asBinder(parent));
+        remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
+        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
     virtual void setTransactionState(
@@ -104,7 +103,7 @@
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform,
             ISurfaceComposer::Rotation rotation)
     {
@@ -115,8 +114,8 @@
         data.write(sourceCrop);
         data.writeUint32(reqWidth);
         data.writeUint32(reqHeight);
-        data.writeUint32(minLayerZ);
-        data.writeUint32(maxLayerZ);
+        data.writeInt32(minLayerZ);
+        data.writeInt32(maxLayerZ);
         data.writeInt32(static_cast<int32_t>(useIdentityTransform));
         data.writeInt32(static_cast<int32_t>(rotation));
         remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
@@ -158,7 +157,51 @@
         return result != 0;
     }
 
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection()
+    virtual status_t getSupportedFrameTimestamps(
+            std::vector<FrameEvent>* outSupported) const {
+        if (!outSupported) {
+            return UNEXPECTED_NULL;
+        }
+        outSupported->clear();
+
+        Parcel data, reply;
+
+        status_t err = data.writeInterfaceToken(
+                ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        err = remote()->transact(
+                BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+                data, &reply);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        int32_t result = 0;
+        err = reply.readInt32(&result);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        std::vector<int32_t> supported;
+        err = reply.readInt32Vector(&supported);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        outSupported->reserve(supported.size());
+        for (int32_t s : supported) {
+            outSupported->push_back(static_cast<FrameEvent>(s));
+        }
+        return NO_ERROR;
+    }
+
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource)
     {
         Parcel data, reply;
         sp<IDisplayEventConnection> result;
@@ -167,6 +210,7 @@
         if (err != NO_ERROR) {
             return result;
         }
+        data.writeInt32(static_cast<int32_t>(vsyncSource));
         err = remote()->transact(
                 BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
                 data, &reply);
@@ -379,10 +423,52 @@
         }
         result = reply.readInt32();
         if (result == NO_ERROR) {
-            result = reply.readParcelable(outCapabilities);
+            result = reply.read(*outCapabilities);
         }
         return result;
     }
+
+    virtual status_t enableVSyncInjections(bool enable) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeBool(enable);
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to writeBool: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
+                data, &reply, TF_ONE_WAY);
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
+    virtual status_t injectVSync(nsecs_t when) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeInt64(when);
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to writeInt64: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -403,9 +489,11 @@
             reply->writeStrongBinder(b);
             return NO_ERROR;
         }
-        case CREATE_GRAPHIC_BUFFER_ALLOC: {
+        case CREATE_SCOPED_CONNECTION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> b = IInterface::asBinder(createGraphicBufferAlloc());
+            sp<IGraphicBufferProducer> bufferProducer =
+                interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+            sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
             reply->writeStrongBinder(b);
             return NO_ERROR;
         }
@@ -458,8 +546,8 @@
             data.read(sourceCrop);
             uint32_t reqWidth = data.readUint32();
             uint32_t reqHeight = data.readUint32();
-            uint32_t minLayerZ = data.readUint32();
-            uint32_t maxLayerZ = data.readUint32();
+            int32_t minLayerZ = data.readInt32();
+            int32_t maxLayerZ = data.readInt32();
             bool useIdentityTransform = static_cast<bool>(data.readInt32());
             int32_t rotation = data.readInt32();
 
@@ -478,9 +566,29 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            std::vector<FrameEvent> supportedTimestamps;
+            status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+            status_t err = reply->writeInt32(result);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            if (result != NO_ERROR) {
+                return result;
+            }
+
+            std::vector<int32_t> supported;
+            supported.reserve(supportedTimestamps.size());
+            for (FrameEvent s : supportedTimestamps) {
+                supported.push_back(static_cast<int32_t>(s));
+            }
+            return reply->writeInt32Vector(supported);
+        }
         case CREATE_DISPLAY_EVENT_CONNECTION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IDisplayEventConnection> connection(createDisplayEventConnection());
+            sp<IDisplayEventConnection> connection(createDisplayEventConnection(
+                    static_cast<ISurfaceComposer::VsyncSource>(data.readInt32())));
             reply->writeStrongBinder(IInterface::asBinder(connection));
             return NO_ERROR;
         }
@@ -631,10 +739,30 @@
             result = getHdrCapabilities(display, &capabilities);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
-                reply->writeParcelable(capabilities);
+                reply->write(capabilities);
             }
             return NO_ERROR;
         }
+        case ENABLE_VSYNC_INJECTIONS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool enable = false;
+            status_t result = data.readBool(&enable);
+            if (result != NO_ERROR) {
+                ALOGE("enableVSyncInjections failed to readBool: %d", result);
+                return result;
+            }
+            return enableVSyncInjections(enable);
+        }
+        case INJECT_VSYNC: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int64_t when = 0;
+            status_t result = data.readInt64(&when);
+            if (result != NO_ERROR) {
+                ALOGE("enableVSyncInjections failed to readInt64: %d", result);
+                return result;
+            }
+            return injectVSync(when);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index dd5b169..679f44b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -17,112 +17,61 @@
 // tag as surfaceflinger
 #define LOG_TAG "SurfaceFlinger"
 
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <ui/Point.h>
-#include <ui/Rect.h>
+#include <gui/ISurfaceComposerClient.h>
 
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/ISurfaceComposerClient.h>
-#include <private/gui/LayerState.h>
 
-// ---------------------------------------------------------------------------
+#include <binder/SafeInterface.h>
+
+#include <ui/FrameStats.h>
 
 namespace android {
 
-enum {
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
     CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
     DESTROY_SURFACE,
     CLEAR_LAYER_FRAME_STATS,
     GET_LAYER_FRAME_STATS,
-    GET_TRANSFORM_TO_DISPLAY_INVERSE
+    LAST = GET_LAYER_FRAME_STATS,
 };
 
-class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
-{
+} // Anonymous namespace
+
+class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> {
 public:
-    BpSurfaceComposerClient(const sp<IBinder>& impl)
-        : BpInterface<ISurfaceComposerClient>(impl) {
+    explicit BpSurfaceComposerClient(const sp<IBinder>& impl)
+          : SafeBpInterface<ISurfaceComposerClient>(impl, "BpSurfaceComposerClient") {}
+
+    ~BpSurfaceComposerClient() override;
+
+    status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
+                           uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
+                           uint32_t ownerUid, sp<IBinder>* handle,
+                           sp<IGraphicBufferProducer>* gbp) override {
+        return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
+                                                                            name, width, height,
+                                                                            format, flags, parent,
+                                                                            windowType, ownerUid,
+                                                                            handle, gbp);
     }
 
-    virtual ~BpSurfaceComposerClient();
-
-    virtual status_t createSurface(const String8& name, uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t flags,
-            sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        data.writeString8(name);
-        data.writeUint32(width);
-        data.writeUint32(height);
-        data.writeInt32(static_cast<int32_t>(format));
-        data.writeUint32(flags);
-        remote()->transact(CREATE_SURFACE, data, &reply);
-        *handle = reply.readStrongBinder();
-        *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
-        return reply.readInt32();
+    status_t destroySurface(const sp<IBinder>& handle) override {
+        return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE,
+                                                                             handle);
     }
 
-    virtual status_t destroySurface(const sp<IBinder>& handle) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        data.writeStrongBinder(handle);
-        remote()->transact(DESTROY_SURFACE, data, &reply);
-        return reply.readInt32();
+    status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
+        return callRemote<decltype(
+                &ISurfaceComposerClient::clearLayerFrameStats)>(Tag::CLEAR_LAYER_FRAME_STATS,
+                                                                handle);
     }
 
-    virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        data.writeStrongBinder(handle);
-        remote()->transact(CLEAR_LAYER_FRAME_STATS, data, &reply);
-        return reply.readInt32();
-    }
-
-    virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        data.writeStrongBinder(handle);
-        remote()->transact(GET_LAYER_FRAME_STATS, data, &reply);
-        reply.read(*outStats);
-        return reply.readInt32();
-    }
-
-    virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
-            bool* outTransformToDisplayInverse) const {
-        Parcel data, reply;
-        status_t result =
-                data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        if (result != NO_ERROR) {
-            return result;
-        }
-        result = data.writeStrongBinder(handle);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        int transformInverse;
-        result = reply.readInt32(&transformInverse);
-        if (result != NO_ERROR) {
-            return result;
-        }
-        *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
-        status_t result2 = reply.readInt32(&result);
-        if (result2 != NO_ERROR) {
-            return result2;
-        }
-        return result;
+    status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const override {
+        return callRemote<decltype(
+                &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
+                                                              outStats);
     }
 };
 
@@ -134,69 +83,22 @@
 
 // ----------------------------------------------------------------------
 
-status_t BnSurfaceComposerClient::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-     switch(code) {
-        case CREATE_SURFACE: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            String8 name = data.readString8();
-            uint32_t width = data.readUint32();
-            uint32_t height = data.readUint32();
-            PixelFormat format = static_cast<PixelFormat>(data.readInt32());
-            uint32_t createFlags = data.readUint32();
-            sp<IBinder> handle;
-            sp<IGraphicBufferProducer> gbp;
-            status_t result = createSurface(name, width, height, format,
-                    createFlags, &handle, &gbp);
-            reply->writeStrongBinder(handle);
-            reply->writeStrongBinder(IInterface::asBinder(gbp));
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case DESTROY_SURFACE: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            reply->writeInt32(destroySurface( data.readStrongBinder() ) );
-            return NO_ERROR;
-        }
-       case CLEAR_LAYER_FRAME_STATS: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            sp<IBinder> handle = data.readStrongBinder();
-            status_t result = clearLayerFrameStats(handle);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case GET_LAYER_FRAME_STATS: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            sp<IBinder> handle = data.readStrongBinder();
-            FrameStats stats;
-            status_t result = getLayerFrameStats(handle, &stats);
-            reply->write(stats);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            sp<IBinder> handle;
-            status_t result = data.readStrongBinder(&handle);
-            if (result != NO_ERROR) {
-                return result;
-            }
-            bool transformInverse = false;
-            result = getTransformToDisplayInverse(handle, &transformInverse);
-            if (result != NO_ERROR) {
-                return result;
-            }
-            result = reply->writeInt32(transformInverse ? 1 : 0);
-            if (result != NO_ERROR) {
-                return result;
-            }
-            result = reply->writeInt32(NO_ERROR);
-            return result;
-        }
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
+status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                             uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::CREATE_SURFACE:
+            return callLocal(data, reply, &ISurfaceComposerClient::createSurface);
+        case Tag::DESTROY_SURFACE:
+            return callLocal(data, reply, &ISurfaceComposerClient::destroySurface);
+        case Tag::CLEAR_LAYER_FRAME_STATS:
+            return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
+        case Tag::GET_LAYER_FRAME_STATS:
+            return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
     }
 }
 
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d1c576e..9b06e63 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -28,7 +28,7 @@
     output.writeUint32(what);
     output.writeFloat(x);
     output.writeFloat(y);
-    output.writeUint32(z);
+    output.writeInt32(z);
     output.writeUint32(w);
     output.writeUint32(h);
     output.writeUint32(layerStack);
@@ -39,9 +39,12 @@
             output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
     output.write(crop);
     output.write(finalCrop);
-    output.writeStrongBinder(handle);
+    output.writeStrongBinder(barrierHandle);
+    output.writeStrongBinder(reparentHandle);
     output.writeUint64(frameNumber);
     output.writeInt32(overrideScalingMode);
+    output.writeStrongBinder(IInterface::asBinder(barrierGbp));
+    output.writeStrongBinder(relativeLayerHandle);
     output.write(transparentRegion);
     return NO_ERROR;
 }
@@ -52,7 +55,7 @@
     what = input.readUint32();
     x = input.readFloat();
     y = input.readFloat();
-    z = input.readUint32();
+    z = input.readInt32();
     w = input.readUint32();
     h = input.readUint32();
     layerStack = input.readUint32();
@@ -67,9 +70,13 @@
     }
     input.read(crop);
     input.read(finalCrop);
-    handle = input.readStrongBinder();
+    barrierHandle = input.readStrongBinder();
+    reparentHandle = input.readStrongBinder();
     frameNumber = input.readUint64();
     overrideScalingMode = input.readInt32();
+    barrierGbp =
+        interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
+    relativeLayerHandle = input.readStrongBinder();
     input.read(transparentRegion);
     return NO_ERROR;
 }
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
deleted file mode 100644
index 053d153..0000000
--- a/libs/gui/Sensor.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include <binder/AppOpsManager.h>
-#include <binder/IServiceManager.h>
-#include <gui/Sensor.h>
-#include <hardware/sensors.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/Flattenable.h>
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/limits.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-Sensor::Sensor(const char * name) :
-        mName(name), mHandle(0), mType(0),
-        mMinValue(0), mMaxValue(0), mResolution(0),
-        mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
-        mFifoMaxEventCount(0), mRequiredAppOp(0),
-        mMaxDelay(0), mFlags(0) {
-}
-
-Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) :
-        Sensor(*hwSensor, uuid_t(), halVersion) {
-}
-
-Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) {
-    mName = hwSensor.name;
-    mVendor = hwSensor.vendor;
-    mVersion = hwSensor.version;
-    mHandle = hwSensor.handle;
-    mType = hwSensor.type;
-    mMinValue = 0;                      // FIXME: minValue
-    mMaxValue = hwSensor.maxRange;     // FIXME: maxValue
-    mResolution = hwSensor.resolution;
-    mPower = hwSensor.power;
-    mMinDelay = hwSensor.minDelay;
-    mFlags = 0;
-    mUuid = uuid;
-
-    // Set fifo event count zero for older devices which do not support batching. Fused
-    // sensors also have their fifo counts set to zero.
-    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
-        mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
-        mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
-    } else {
-        mFifoReservedEventCount = 0;
-        mFifoMaxEventCount = 0;
-    }
-
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        if (hwSensor.maxDelay > INT_MAX) {
-            // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
-            // always fit in a 32 bit integer, log error and cap it to INT_MAX.
-            ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
-                  static_cast<int64_t>(hwSensor.maxDelay));
-            mMaxDelay = INT_MAX;
-        } else {
-            mMaxDelay = static_cast<int32_t>(hwSensor.maxDelay);
-        }
-    } else {
-        // For older hals set maxDelay to 0.
-        mMaxDelay = 0;
-    }
-
-    // Ensure existing sensors have correct string type, required permissions and reporting mode.
-    // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity
-    // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older
-    // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors.
-    // All the OEM defined defined sensors have flags set to whatever is provided by the HAL.
-    switch (mType) {
-    case SENSOR_TYPE_ACCELEROMETER:
-        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_AMBIENT_TEMPERATURE:
-        mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_GAME_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GRAVITY:
-        mStringType = SENSOR_STRING_TYPE_GRAVITY;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GYROSCOPE:
-        mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
-        mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_HEART_RATE: {
-        mStringType = SENSOR_STRING_TYPE_HEART_RATE;
-        mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
-        AppOpsManager appOps;
-        mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        } break;
-    case SENSOR_TYPE_LIGHT:
-        mStringType = SENSOR_STRING_TYPE_LIGHT;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_LINEAR_ACCELERATION:
-        mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_MAGNETIC_FIELD:
-        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_ORIENTATION:
-        mStringType = SENSOR_STRING_TYPE_ORIENTATION;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_PRESSURE:
-        mStringType = SENSOR_STRING_TYPE_PRESSURE;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_PROXIMITY:
-        mStringType = SENSOR_STRING_TYPE_PROXIMITY;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_RELATIVE_HUMIDITY:
-        mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_SIGNIFICANT_MOTION:
-        mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_STEP_COUNTER:
-        mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_STEP_DETECTOR:
-        mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        break;
-    case SENSOR_TYPE_TEMPERATURE:
-        mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_TILT_DETECTOR:
-        mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_WAKE_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_GLANCE_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_PICK_UP_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_WRIST_TILT_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_DYNAMIC_SENSOR_META:
-        mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
-        mFlags = SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger and non-wake up
-        break;
-    case SENSOR_TYPE_POSE_6DOF:
-        mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_STATIONARY_DETECT:
-        mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_MOTION_DETECT:
-        mStringType = SENSOR_STRING_TYPE_MOTION_DETECT;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_HEART_BEAT:
-        mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        break;
-    default:
-        // Only pipe the stringType, requiredPermission and flags for custom sensors.
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
-            mStringType = hwSensor.stringType;
-        }
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
-            mRequiredPermission = hwSensor.requiredPermission;
-            if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
-                AppOpsManager appOps;
-                mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
-            }
-        }
-
-        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags = static_cast<uint32_t>(hwSensor.flags);
-        } else {
-            // This is an OEM defined sensor on an older HAL. Use minDelay to determine the
-            // reporting mode of the sensor.
-            if (mMinDelay > 0) {
-                mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-            } else if (mMinDelay == 0) {
-                mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-            } else if (mMinDelay < 0) {
-                mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-            }
-        }
-        break;
-    }
-
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        // Wake-up flag of HAL 1.3 and above is set here
-        mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP);
-
-        // Log error if the reporting mode is not as expected, but respect HAL setting.
-        int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
-        int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
-        if (actualReportingMode != expectedReportingMode) {
-            ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
-                   "actual=%d expected=%d",
-                   mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
-        }
-    }
-
-    // Feature flags
-    // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        mFlags |= (hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK));
-    }
-    // Set DATA_INJECTION flag here. Defined in HAL 1_4.
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
-        mFlags |= (hwSensor.flags & DATA_INJECTION_MASK);
-    }
-
-    if (mRequiredPermission.length() > 0) {
-        // If the sensor is protected by a permission we need to know if it is
-        // a runtime one to determine whether we can use the permission cache.
-        sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-        if (binder != 0) {
-            sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
-            mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
-                    String16(mRequiredPermission));
-        }
-    }
-}
-
-Sensor::~Sensor() {
-}
-
-const String8& Sensor::getName() const {
-    return mName;
-}
-
-const String8& Sensor::getVendor() const {
-    return mVendor;
-}
-
-int32_t Sensor::getHandle() const {
-    return mHandle;
-}
-
-int32_t Sensor::getType() const {
-    return mType;
-}
-
-float Sensor::getMinValue() const {
-    return mMinValue;
-}
-
-float Sensor::getMaxValue() const {
-    return mMaxValue;
-}
-
-float Sensor::getResolution() const {
-    return mResolution;
-}
-
-float Sensor::getPowerUsage() const {
-    return mPower;
-}
-
-int32_t Sensor::getMinDelay() const {
-    return mMinDelay;
-}
-
-nsecs_t Sensor::getMinDelayNs() const {
-    return getMinDelay() * 1000;
-}
-
-int32_t Sensor::getVersion() const {
-    return mVersion;
-}
-
-uint32_t Sensor::getFifoReservedEventCount() const {
-    return mFifoReservedEventCount;
-}
-
-uint32_t Sensor::getFifoMaxEventCount() const {
-    return mFifoMaxEventCount;
-}
-
-const String8& Sensor::getStringType() const {
-    return mStringType;
-}
-
-const String8& Sensor::getRequiredPermission() const {
-    return mRequiredPermission;
-}
-
-bool Sensor::isRequiredPermissionRuntime() const {
-    return mRequiredPermissionRuntime;
-}
-
-int32_t Sensor::getRequiredAppOp() const {
-    return mRequiredAppOp;
-}
-
-int32_t Sensor::getMaxDelay() const {
-    return mMaxDelay;
-}
-
-uint32_t Sensor::getFlags() const {
-    return mFlags;
-}
-
-bool Sensor::isWakeUpSensor() const {
-    return (mFlags & SENSOR_FLAG_WAKE_UP) != 0;
-}
-
-bool Sensor::isDynamicSensor() const {
-    return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
-}
-
-bool Sensor::hasAdditionalInfo() const {
-    return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
-}
-
-int32_t Sensor::getReportingMode() const {
-    return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
-}
-
-const Sensor::uuid_t& Sensor::getUuid() const {
-    return mUuid;
-}
-
-void Sensor::setId(int32_t id) {
-    mUuid.i64[0] = id;
-    mUuid.i64[1] = 0;
-}
-
-int32_t Sensor::getId() const {
-    return int32_t(mUuid.i64[0]);
-}
-
-size_t Sensor::getFlattenedSize() const {
-    size_t fixedSize =
-            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
-            sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
-            sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
-            sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
-            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
-
-    size_t variableSize =
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
-
-    return fixedSize + variableSize;
-}
-
-status_t Sensor::flatten(void* buffer, size_t size) const {
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    flattenString8(buffer, size, mName);
-    flattenString8(buffer, size, mVendor);
-    FlattenableUtils::write(buffer, size, mVersion);
-    FlattenableUtils::write(buffer, size, mHandle);
-    FlattenableUtils::write(buffer, size, mType);
-    FlattenableUtils::write(buffer, size, mMinValue);
-    FlattenableUtils::write(buffer, size, mMaxValue);
-    FlattenableUtils::write(buffer, size, mResolution);
-    FlattenableUtils::write(buffer, size, mPower);
-    FlattenableUtils::write(buffer, size, mMinDelay);
-    FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
-    FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
-    flattenString8(buffer, size, mStringType);
-    flattenString8(buffer, size, mRequiredPermission);
-    FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime);
-    FlattenableUtils::write(buffer, size, mRequiredAppOp);
-    FlattenableUtils::write(buffer, size, mMaxDelay);
-    FlattenableUtils::write(buffer, size, mFlags);
-    if (mUuid.i64[1] != 0) {
-        // We should never hit this case with our current API, but we
-        // could via a careless API change.  If that happens,
-        // this code will keep us from leaking our UUID (while probably
-        // breaking dynamic sensors).  See b/29547335.
-        ALOGW("Sensor with UUID being flattened; sending 0.  Expect "
-              "bad dynamic sensor behavior");
-        uuid_t tmpUuid;  // default constructor makes this 0.
-        FlattenableUtils::write(buffer, size, tmpUuid);
-    } else {
-        FlattenableUtils::write(buffer, size, mUuid);
-    }
-    return NO_ERROR;
-}
-
-status_t Sensor::unflatten(void const* buffer, size_t size) {
-    if (!unflattenString8(buffer, size, mName)) {
-        return NO_MEMORY;
-    }
-    if (!unflattenString8(buffer, size, mVendor)) {
-        return NO_MEMORY;
-    }
-
-    size_t fixedSize1 =
-            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) +
-            sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) +
-            sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount);
-    if (size < fixedSize1) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, mVersion);
-    FlattenableUtils::read(buffer, size, mHandle);
-    FlattenableUtils::read(buffer, size, mType);
-    FlattenableUtils::read(buffer, size, mMinValue);
-    FlattenableUtils::read(buffer, size, mMaxValue);
-    FlattenableUtils::read(buffer, size, mResolution);
-    FlattenableUtils::read(buffer, size, mPower);
-    FlattenableUtils::read(buffer, size, mMinDelay);
-    FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
-    FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
-
-    if (!unflattenString8(buffer, size, mStringType)) {
-        return NO_MEMORY;
-    }
-    if (!unflattenString8(buffer, size, mRequiredPermission)) {
-        return NO_MEMORY;
-    }
-
-    size_t fixedSize2 =
-            sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
-            sizeof(mFlags) + sizeof(mUuid);
-    if (size < fixedSize2) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime);
-    FlattenableUtils::read(buffer, size, mRequiredAppOp);
-    FlattenableUtils::read(buffer, size, mMaxDelay);
-    FlattenableUtils::read(buffer, size, mFlags);
-    FlattenableUtils::read(buffer, size, mUuid);
-    return NO_ERROR;
-}
-
-void Sensor::flattenString8(void*& buffer, size_t& size,
-        const String8& string8) {
-    uint32_t len = static_cast<uint32_t>(string8.length());
-    FlattenableUtils::write(buffer, size, len);
-    memcpy(static_cast<char*>(buffer), string8.string(), len);
-    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-}
-
-bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
-    uint32_t len;
-    if (size < sizeof(len)) {
-        return false;
-    }
-    FlattenableUtils::read(buffer, size, len);
-    if (size < len) {
-        return false;
-    }
-    outputString8.setTo(static_cast<char const*>(buffer), len);
-    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-    return true;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
deleted file mode 100644
index 6d69839..0000000
--- a/libs/gui/SensorEventQueue.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <algorithm>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/errno.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/SensorEventQueue.h>
-#include <gui/ISensorEventConnection.h>
-
-#include <android/sensor.h>
-
-using std::min;
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
-    : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
-      mNumAcksToSend(0) {
-    mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
-}
-
-SensorEventQueue::~SensorEventQueue() {
-    delete [] mRecBuffer;
-}
-
-void SensorEventQueue::onFirstRef()
-{
-    mSensorChannel = mSensorEventConnection->getSensorChannel();
-}
-
-int SensorEventQueue::getFd() const
-{
-    return mSensorChannel->getFd();
-}
-
-
-ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
-        ASensorEvent const* events, size_t numEvents) {
-    return BitTube::sendObjects(tube, events, numEvents);
-}
-
-ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
-    if (mAvailable == 0) {
-        ssize_t err = BitTube::recvObjects(mSensorChannel,
-                mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
-        if (err < 0) {
-            return err;
-        }
-        mAvailable = static_cast<size_t>(err);
-        mConsumed = 0;
-    }
-    size_t count = min(numEvents, mAvailable);
-    memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
-    mAvailable -= count;
-    mConsumed += count;
-    return static_cast<ssize_t>(count);
-}
-
-sp<Looper> SensorEventQueue::getLooper() const
-{
-    Mutex::Autolock _l(mLock);
-    if (mLooper == 0) {
-        mLooper = new Looper(true);
-        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
-    }
-    return mLooper;
-}
-
-status_t SensorEventQueue::waitForEvent() const
-{
-    const int fd = getFd();
-    sp<Looper> looper(getLooper());
-
-    int events;
-    int32_t result;
-    do {
-        result = looper->pollOnce(-1, NULL, &events, NULL);
-        if (result == ALOOPER_POLL_ERROR) {
-            ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
-            result = -EPIPE; // unknown error, so we make up one
-            break;
-        }
-        if (events & ALOOPER_EVENT_HANGUP) {
-            // the other-side has died
-            ALOGE("SensorEventQueue::waitForEvent error HANGUP");
-            result = -EPIPE; // unknown error, so we make up one
-            break;
-        }
-    } while (result != fd);
-
-    return  (result == fd) ? status_t(NO_ERROR) : result;
-}
-
-status_t SensorEventQueue::wake() const
-{
-    sp<Looper> looper(getLooper());
-    looper->wake();
-    return NO_ERROR;
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
-    return enableSensor(sensor, SENSOR_DELAY_NORMAL);
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const {
-    return mSensorEventConnection->enableDisable(sensor->getHandle(), true,
-                                                 us2ns(samplingPeriodUs), 0, 0);
-}
-
-status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
-    return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0);
-}
-
-status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
-                                        int maxBatchReportLatencyUs, int reservedFlags) const {
-    return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
-                                                 us2ns(maxBatchReportLatencyUs), reservedFlags);
-}
-
-status_t SensorEventQueue::flush() const {
-    return mSensorEventConnection->flush();
-}
-
-status_t SensorEventQueue::disableSensor(int32_t handle) const {
-    return mSensorEventConnection->enableDisable(handle, false, 0, 0, false);
-}
-
-status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
-    return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
-}
-
-status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
-    do {
-        // Blocking call.
-        ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
-        if (size >= 0) {
-            return NO_ERROR;
-        } else if (size < 0 && errno == EAGAIN) {
-            // If send is returning a "Try again" error, sleep for 100ms and try again. In all
-            // other cases log a failure and exit.
-            usleep(100000);
-        } else {
-            ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size);
-            return INVALID_OPERATION;
-        }
-    } while (true);
-}
-
-void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
-    for (int i = 0; i < count; ++i) {
-        if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {
-            ++mNumAcksToSend;
-        }
-    }
-    // Send mNumAcksToSend to acknowledge for the wake up sensor events received.
-    if (mNumAcksToSend > 0) {
-        ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend),
-                MSG_DONTWAIT | MSG_NOSIGNAL);
-        if (size < 0) {
-            ALOGE("sendAck failure %zd %d", size, mNumAcksToSend);
-        } else {
-            mNumAcksToSend = 0;
-        }
-    }
-    return;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
deleted file mode 100644
index 9fcf9ab..0000000
--- a/libs/gui/SensorManager.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-
-#include <binder/IBinder.h>
-#include <binder/IServiceManager.h>
-
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
-
-SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
-    Mutex::Autolock _l(sLock);
-    SensorManager* sensorManager;
-    std::map<String16, SensorManager*>::iterator iterator =
-        sPackageInstances.find(packageName);
-
-    if (iterator != sPackageInstances.end()) {
-        sensorManager = iterator->second;
-    } else {
-        String16 opPackageName = packageName;
-
-        // It is possible that the calling code has no access to the package name.
-        // In this case we will get the packages for the calling UID and pick the
-        // first one for attributing the app op. This will work correctly for
-        // runtime permissions as for legacy apps we will toggle the app op for
-        // all packages in the UID. The caveat is that the operation may be attributed
-        // to the wrong package and stats based on app ops may be slightly off.
-        if (opPackageName.size() <= 0) {
-            sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-            if (binder != 0) {
-                const uid_t uid = IPCThreadState::self()->getCallingUid();
-                Vector<String16> packages;
-                interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
-                if (!packages.isEmpty()) {
-                    opPackageName = packages[0];
-                } else {
-                    ALOGE("No packages for calling UID");
-                }
-            } else {
-                ALOGE("Cannot get permission service");
-            }
-        }
-
-        sensorManager = new SensorManager(opPackageName);
-
-        // If we had no package name, we looked it up from the UID and the sensor
-        // manager instance we created should also be mapped to the empty package
-        // name, to avoid looking up the packages for a UID and get the same result.
-        if (packageName.size() <= 0) {
-            sPackageInstances.insert(std::make_pair(String16(), sensorManager));
-        }
-
-        // Stash the per package sensor manager.
-        sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
-    }
-
-    return *sensorManager;
-}
-
-SensorManager::SensorManager(const String16& opPackageName)
-    : mSensorList(0), mOpPackageName(opPackageName) {
-    // okay we're not locked here, but it's not needed during construction
-    assertStateLocked();
-}
-
-SensorManager::~SensorManager() {
-    free(mSensorList);
-}
-
-void SensorManager::sensorManagerDied() {
-    Mutex::Autolock _l(mLock);
-    mSensorServer.clear();
-    free(mSensorList);
-    mSensorList = NULL;
-    mSensors.clear();
-}
-
-status_t SensorManager::assertStateLocked() {
-    bool initSensorManager = false;
-    if (mSensorServer == NULL) {
-        initSensorManager = true;
-    } else {
-        // Ping binder to check if sensorservice is alive.
-        status_t err = IInterface::asBinder(mSensorServer)->pingBinder();
-        if (err != NO_ERROR) {
-            initSensorManager = true;
-        }
-    }
-    if (initSensorManager) {
-        // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
-        const String16 name("sensorservice");
-        for (int i = 0; i < 60; i++) {
-            status_t err = getService(name, &mSensorServer);
-            if (err == NAME_NOT_FOUND) {
-                sleep(1);
-                continue;
-            }
-            if (err != NO_ERROR) {
-                return err;
-            }
-            break;
-        }
-
-        class DeathObserver : public IBinder::DeathRecipient {
-            SensorManager& mSensorManager;
-            virtual void binderDied(const wp<IBinder>& who) {
-                ALOGW("sensorservice died [%p]", who.unsafe_get());
-                mSensorManager.sensorManagerDied();
-            }
-        public:
-            DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
-        };
-
-        LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL");
-
-        mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
-        IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
-
-        mSensors = mSensorServer->getSensorList(mOpPackageName);
-        size_t count = mSensors.size();
-        mSensorList =
-                static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
-        LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
-
-        for (size_t i=0 ; i<count ; i++) {
-            mSensorList[i] = mSensors.array() + i;
-        }
-    }
-
-    return NO_ERROR;
-}
-
-ssize_t SensorManager::getSensorList(Sensor const* const** list) {
-    Mutex::Autolock _l(mLock);
-    status_t err = assertStateLocked();
-    if (err < 0) {
-        return static_cast<ssize_t>(err);
-    }
-    *list = mSensorList;
-    return static_cast<ssize_t>(mSensors.size());
-}
-
-ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
-    Mutex::Autolock _l(mLock);
-    status_t err = assertStateLocked();
-    if (err < 0) {
-        return static_cast<ssize_t>(err);
-    }
-
-    dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName);
-    size_t count = dynamicSensors.size();
-
-    return static_cast<ssize_t>(count);
-}
-
-Sensor const* SensorManager::getDefaultSensor(int type)
-{
-    Mutex::Autolock _l(mLock);
-    if (assertStateLocked() == NO_ERROR) {
-        bool wakeUpSensor = false;
-        // For the following sensor types, return a wake-up sensor. These types are by default
-        // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
-        // a non_wake-up version.
-        if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
-            type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
-            type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
-            type == SENSOR_TYPE_WRIST_TILT_GESTURE) {
-            wakeUpSensor = true;
-        }
-        // For now we just return the first sensor of that type we find.
-        // in the future it will make sense to let the SensorService make
-        // that decision.
-        for (size_t i=0 ; i<mSensors.size() ; i++) {
-            if (mSensorList[i]->getType() == type &&
-                mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
-                return mSensorList[i];
-            }
-        }
-    }
-    return NULL;
-}
-
-sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
-    sp<SensorEventQueue> queue;
-
-    Mutex::Autolock _l(mLock);
-    while (assertStateLocked() == NO_ERROR) {
-        sp<ISensorEventConnection> connection =
-                mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
-        if (connection == NULL) {
-            // SensorService just died or the app doesn't have required permissions.
-            ALOGE("createEventQueue: connection is NULL.");
-            return NULL;
-        }
-        queue = new SensorEventQueue(connection);
-        break;
-    }
-    return queue;
-}
-
-bool SensorManager::isDataInjectionEnabled() {
-    Mutex::Autolock _l(mLock);
-    if (assertStateLocked() == NO_ERROR) {
-        return mSensorServer->isDataInjectionEnabled();
-    }
-    return false;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 5a2ca8d..7b2b5c3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -18,25 +18,28 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
-#include <android/native_window.h>
+#include <gui/Surface.h>
 
-#include <binder/Parcel.h>
+#include <android/native_window.h>
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <utils/NativeHandle.h>
 
+#include <ui/DisplayStatInfo.h>
 #include <ui/Fence.h>
+#include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
 
+#include <gui/BufferItem.h>
 #include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/GLConsumer.h>
-#include <gui/Surface.h>
 
+#include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 namespace android {
 
 Surface::Surface(
@@ -49,7 +52,10 @@
       mAutoRefresh(false),
       mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
       mSharedBufferHasBeenQueued(false),
-      mNextFrameNumber(1)
+      mQueriedSupportedTimestamps(false),
+      mFrameTimestampsSupportsPresent(false),
+      mEnableFrameTimestamps(false),
+      mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -93,6 +99,14 @@
     }
 }
 
+sp<ISurfaceComposer> Surface::composerService() const {
+    return ComposerService::getComposerService();
+}
+
+nsecs_t Surface::now() const {
+    return systemTime();
+}
+
 sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
     return mGraphicBufferProducer;
 }
@@ -135,37 +149,224 @@
             outTransformMatrix);
 }
 
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
-        nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
-        nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) {
+    ATRACE_CALL();
+
+    DisplayStatInfo stats;
+    status_t err = composerService()->getDisplayStats(NULL, &stats);
+
+    *outRefreshDuration = stats.vsyncPeriod;
+
+    return NO_ERROR;
+}
+
+void Surface::enableFrameTimestamps(bool enable) {
+    Mutex::Autolock lock(mMutex);
+    // If going from disabled to enabled, get the initial values for
+    // compositor and display timing.
+    if (!mEnableFrameTimestamps && enable) {
+        FrameEventHistoryDelta delta;
+        mGraphicBufferProducer->getFrameTimestamps(&delta);
+        mFrameEventHistory->applyDelta(delta);
+    }
+    mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getCompositorTiming(
+        nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+        nsecs_t* compositeToPresentLatency) {
+    Mutex::Autolock lock(mMutex);
+    if (!mEnableFrameTimestamps) {
+        return INVALID_OPERATION;
+    }
+
+    if (compositeDeadline != nullptr) {
+        *compositeDeadline =
+                mFrameEventHistory->getNextCompositeDeadline(now());
+    }
+    if (compositeInterval != nullptr) {
+        *compositeInterval = mFrameEventHistory->getCompositeInterval();
+    }
+    if (compositeToPresentLatency != nullptr) {
+        *compositeToPresentLatency =
+                mFrameEventHistory->getCompositeToPresentLatency();
+    }
+    return NO_ERROR;
+}
+
+static bool checkConsumerForUpdates(
+        const FrameEvents* e, const uint64_t lastFrameNumber,
+        const nsecs_t* outLatchTime,
+        const nsecs_t* outFirstRefreshStartTime,
+        const nsecs_t* outLastRefreshStartTime,
+        const nsecs_t* outGpuCompositionDoneTime,
+        const nsecs_t* outDisplayPresentTime,
+        const nsecs_t* outDequeueReadyTime,
+        const nsecs_t* outReleaseTime) {
+    bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+    bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+            !e->hasFirstRefreshStartInfo();
+    bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) &&
+            !e->hasGpuCompositionDoneInfo();
+    bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+            !e->hasDisplayPresentInfo();
+
+    // LastRefreshStart, DequeueReady, and Release are never available for the
+    // last frame.
+    bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+            !e->hasLastRefreshStartInfo() &&
+            (e->frameNumber != lastFrameNumber);
+    bool checkForDequeueReady = (outDequeueReadyTime != nullptr) &&
+            !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber);
+    bool checkForRelease = (outReleaseTime != nullptr) &&
+            !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber);
+
+    // RequestedPresent and Acquire info are always available producer-side.
+    return checkForLatch || checkForFirstRefreshStart ||
+            checkForLastRefreshStart || checkForGpuCompositionDone ||
+            checkForDisplayPresent || checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+    if (dst != nullptr) {
+        // We always get valid timestamps for these eventually.
+        *dst = (src == FrameEvents::TIMESTAMP_PENDING) ?
+                NATIVE_WINDOW_TIMESTAMP_PENDING : src;
+    }
+}
+
+static void getFrameTimestampFence(nsecs_t *dst,
+        const std::shared_ptr<FenceTime>& src, bool fenceShouldBeKnown) {
+    if (dst != nullptr) {
+        if (!fenceShouldBeKnown) {
+            *dst = NATIVE_WINDOW_TIMESTAMP_PENDING;
+            return;
+        }
+
+        nsecs_t signalTime = src->getSignalTime();
+        *dst = (signalTime == Fence::SIGNAL_TIME_PENDING) ?
+                    NATIVE_WINDOW_TIMESTAMP_PENDING :
+                (signalTime == Fence::SIGNAL_TIME_INVALID) ?
+                    NATIVE_WINDOW_TIMESTAMP_INVALID :
+                signalTime;
+    }
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
+        nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+        nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+        nsecs_t* outLastRefreshStartTime, nsecs_t* outGpuCompositionDoneTime,
+        nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
         nsecs_t* outReleaseTime) {
     ATRACE_CALL();
 
-    FrameTimestamps timestamps;
-    bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
-            &timestamps);
-    if (found) {
-        if (outPostedTime) {
-            *outPostedTime = timestamps.postedTime;
-        }
-        if (outAcquireTime) {
-            *outAcquireTime = timestamps.acquireTime;
-        }
-        if (outRefreshStartTime) {
-            *outRefreshStartTime = timestamps.refreshStartTime;
-        }
-        if (outGlCompositionDoneTime) {
-            *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
-        }
-        if (outDisplayRetireTime) {
-            *outDisplayRetireTime = timestamps.displayRetireTime;
-        }
-        if (outReleaseTime) {
-            *outReleaseTime = timestamps.releaseTime;
-        }
-        return true;
+    Mutex::Autolock lock(mMutex);
+
+    if (!mEnableFrameTimestamps) {
+        return INVALID_OPERATION;
     }
-    return false;
+
+    // Verify the requested timestamps are supported.
+    querySupportedTimestampsLocked();
+    if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) {
+        return BAD_VALUE;
+    }
+
+    FrameEvents* events = mFrameEventHistory->getFrame(frameNumber);
+    if (events == nullptr) {
+        // If the entry isn't available in the producer, it's definitely not
+        // available in the consumer.
+        return NAME_NOT_FOUND;
+    }
+
+    // Update our cache of events if the requested events are not available.
+    if (checkConsumerForUpdates(events, mLastFrameNumber,
+            outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGpuCompositionDoneTime, outDisplayPresentTime,
+            outDequeueReadyTime, outReleaseTime)) {
+        FrameEventHistoryDelta delta;
+        mGraphicBufferProducer->getFrameTimestamps(&delta);
+        mFrameEventHistory->applyDelta(delta);
+        events = mFrameEventHistory->getFrame(frameNumber);
+    }
+
+    if (events == nullptr) {
+        // The entry was available before the update, but was overwritten
+        // after the update. Make sure not to send the wrong frame's data.
+        return NAME_NOT_FOUND;
+    }
+
+    getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime);
+    getFrameTimestamp(outLatchTime, events->latchTime);
+    getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime);
+    getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime);
+    getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime);
+
+    getFrameTimestampFence(outAcquireTime, events->acquireFence,
+            events->hasAcquireInfo());
+    getFrameTimestampFence(outGpuCompositionDoneTime,
+            events->gpuCompositionDoneFence,
+            events->hasGpuCompositionDoneInfo());
+    getFrameTimestampFence(outDisplayPresentTime, events->displayPresentFence,
+            events->hasDisplayPresentInfo());
+    getFrameTimestampFence(outReleaseTime, events->releaseFence,
+            events->hasReleaseInfo());
+
+    return NO_ERROR;
+}
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+status_t Surface::getWideColorSupport(bool* supported) {
+    ATRACE_CALL();
+
+    sp<IBinder> display(
+        composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    Vector<android_color_mode_t> colorModes;
+    status_t err =
+        composerService()->getDisplayColorModes(display, &colorModes);
+
+    if (err)
+        return err;
+
+    bool wideColorBoardConfig =
+        getBool<ISurfaceFlingerConfigs,
+                &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+    *supported = false;
+    for (android_color_mode_t colorMode : colorModes) {
+        switch (colorMode) {
+            case HAL_COLOR_MODE_DISPLAY_P3:
+            case HAL_COLOR_MODE_ADOBE_RGB:
+            case HAL_COLOR_MODE_DCI_P3:
+                if (wideColorBoardConfig) {
+                    *supported = true;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t Surface::getHdrSupport(bool* supported) {
+    ATRACE_CALL();
+
+    sp<IBinder> display(
+        composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    HdrCapabilities hdrCapabilities;
+    status_t err =
+        composerService()->getHdrCapabilities(display, &hdrCapabilities);
+
+    if (err)
+        return err;
+
+    *supported = !hdrCapabilities.getSupportedHdrTypes().empty();
+
+    return NO_ERROR;
 }
 
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,9 +472,13 @@
     uint32_t reqHeight;
     PixelFormat reqFormat;
     uint32_t reqUsage;
+    bool enableFrameTimestamps;
 
     {
         Mutex::Autolock lock(mMutex);
+        if (mReportRemovedBuffers) {
+            mRemovedBuffers.clear();
+        }
 
         reqWidth = mReqWidth ? mReqWidth : mUserWidth;
         reqHeight = mReqHeight ? mReqHeight : mUserHeight;
@@ -281,6 +486,8 @@
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
 
+        enableFrameTimestamps = mEnableFrameTimestamps;
+
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
             sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -294,10 +501,13 @@
 
     int buf = -1;
     sp<Fence> fence;
-    nsecs_t now = systemTime();
+    nsecs_t startTime = systemTime();
+
+    FrameEventHistoryDelta frameTimestamps;
     status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
-            reqWidth, reqHeight, reqFormat, reqUsage);
-    mLastDequeueDuration = systemTime() - now;
+            reqWidth, reqHeight, reqFormat, reqUsage,
+            enableFrameTimestamps ? &frameTimestamps : nullptr);
+    mLastDequeueDuration = systemTime() - startTime;
 
     if (result < 0) {
         ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -314,6 +524,9 @@
 
     Mutex::Autolock lock(mMutex);
 
+    // Write this while holding the mutex
+    mLastDequeueStartTime = startTime;
+
     sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
 
     // this should never happen
@@ -323,7 +536,14 @@
         freeAllBuffers();
     }
 
-    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+    if (enableFrameTimestamps) {
+         mFrameEventHistory->applyDelta(frameTimestamps);
+    }
+
+    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+        if (mReportRemovedBuffers && (gbuf != nullptr)) {
+            mRemovedBuffers.push_back(gbuf);
+        }
         result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
         if (result != NO_ERROR) {
             ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
@@ -414,7 +634,7 @@
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         isAutoTimestamp = true;
         ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
-            timestamp / 1000000.f);
+            timestamp / 1000000.0);
     } else {
         timestamp = mTimestamp;
     }
@@ -441,7 +661,7 @@
     IGraphicBufferProducer::QueueBufferOutput output;
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
-            fence, mStickyTransform);
+            fence, mStickyTransform, mEnableFrameTimestamps);
 
     if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
         input.setSurfaceDamage(Region::INVALID_REGION);
@@ -513,17 +733,31 @@
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
 
-    uint32_t numPendingBuffers = 0;
-    uint32_t hint = 0;
-    output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
-            &numPendingBuffers, &mNextFrameNumber);
+    if (mEnableFrameTimestamps) {
+        mFrameEventHistory->applyDelta(output.frameTimestamps);
+        // Update timestamps with the local acquire fence.
+        // The consumer doesn't send it back to prevent us from having two
+        // file descriptors of the same fence.
+        mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
+                std::make_shared<FenceTime>(std::move(fence)));
+
+        // Cache timestamps of signaled fences so we can close their file
+        // descriptors.
+        mFrameEventHistory->updateSignalTimes();
+    }
+
+    mLastFrameNumber = mNextFrameNumber;
+
+    mDefaultWidth = output.width;
+    mDefaultHeight = output.height;
+    mNextFrameNumber = output.nextFrameNumber;
 
     // Disable transform hint if sticky transform is set.
     if (mStickyTransform == 0) {
-        mTransformHint = hint;
+        mTransformHint = output.transformHint;
     }
 
-    mConsumerRunningBehind = (numPendingBuffers >= 2);
+    mConsumerRunningBehind = (output.numPendingBuffers >= 2);
 
     if (!mConnectedToCpu) {
         // Clear surface damage back to full-buffer
@@ -539,6 +773,29 @@
     return err;
 }
 
+void Surface::querySupportedTimestampsLocked() const {
+    // mMutex must be locked when calling this method.
+
+    if (mQueriedSupportedTimestamps) {
+        return;
+    }
+    mQueriedSupportedTimestamps = true;
+
+    std::vector<FrameEvent> supportedFrameTimestamps;
+    status_t err = composerService()->getSupportedFrameTimestamps(
+            &supportedFrameTimestamps);
+
+    if (err != NO_ERROR) {
+        return;
+    }
+
+    for (auto sft : supportedFrameTimestamps) {
+        if (sft == FrameEvent::DISPLAY_PRESENT) {
+            mFrameTimestampsSupportsPresent = true;
+        }
+    }
+}
+
 int Surface::query(int what, int* value) const {
     ATRACE_CALL();
     ALOGV("Surface::query");
@@ -552,9 +809,8 @@
                 }
                 break;
             case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
-                sp<ISurfaceComposer> composer(
-                        ComposerService::getComposerService());
-                if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
+                if (composerService()->authenticateSurfaceTexture(
+                        mGraphicBufferProducer)) {
                     *value = 1;
                 } else {
                     *value = 0;
@@ -601,6 +857,15 @@
                         static_cast<int>(durationUs);
                 return NO_ERROR;
             }
+            case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: {
+                querySupportedTimestampsLocked();
+                *value = mFrameTimestampsSupportsPresent ? 1 : 0;
+                return NO_ERROR;
+            }
+            case NATIVE_WINDOW_IS_VALID: {
+                *value = mGraphicBufferProducer != nullptr ? 1 : 0;
+                return NO_ERROR;
+            }
         }
     }
     return mGraphicBufferProducer->query(what, value);
@@ -676,9 +941,27 @@
     case NATIVE_WINDOW_SET_AUTO_REFRESH:
         res = dispatchSetAutoRefresh(args);
         break;
+    case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION:
+        res = dispatchGetDisplayRefreshCycleDuration(args);
+        break;
+    case NATIVE_WINDOW_GET_NEXT_FRAME_ID:
+        res = dispatchGetNextFrameId(args);
+        break;
+    case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+        res = dispatchEnableFrameTimestamps(args);
+        break;
+    case NATIVE_WINDOW_GET_COMPOSITOR_TIMING:
+        res = dispatchGetCompositorTiming(args);
+        break;
     case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
         res = dispatchGetFrameTimestamps(args);
         break;
+    case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT:
+        res = dispatchGetWideColorSupport(args);
+        break;
+    case NATIVE_WINDOW_GET_HDR_SUPPORT:
+        res = dispatchGetHdrSupport(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -799,18 +1082,57 @@
     return setAutoRefresh(autoRefresh);
 }
 
+int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) {
+    nsecs_t* outRefreshDuration = va_arg(args, int64_t*);
+    return getDisplayRefreshCycleDuration(outRefreshDuration);
+}
+
+int Surface::dispatchGetNextFrameId(va_list args) {
+    uint64_t* nextFrameId = va_arg(args, uint64_t*);
+    *nextFrameId = getNextFrameNumber();
+    return NO_ERROR;
+}
+
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+    bool enable = va_arg(args, int);
+    enableFrameTimestamps(enable);
+    return NO_ERROR;
+}
+
+int Surface::dispatchGetCompositorTiming(va_list args) {
+    nsecs_t* compositeDeadline = va_arg(args, int64_t*);
+    nsecs_t* compositeInterval = va_arg(args, int64_t*);
+    nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*);
+    return getCompositorTiming(compositeDeadline, compositeInterval,
+            compositeToPresentLatency);
+}
+
 int Surface::dispatchGetFrameTimestamps(va_list args) {
-    uint32_t framesAgo = va_arg(args, uint32_t);
-    nsecs_t* outPostedTime = va_arg(args, int64_t*);
+    uint64_t frameId = va_arg(args, uint64_t);
+    nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
     nsecs_t* outAcquireTime = va_arg(args, int64_t*);
-    nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
-    nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
-    nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+    nsecs_t* outLatchTime = va_arg(args, int64_t*);
+    nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outGpuCompositionDoneTime = va_arg(args, int64_t*);
+    nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
+    nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
     nsecs_t* outReleaseTime = va_arg(args, int64_t*);
-    bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
-            outPostedTime, outAcquireTime, outRefreshStartTime,
-            outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
-    return ret ? NO_ERROR : BAD_VALUE;
+    return getFrameTimestamps(frameId,
+            outRequestedPresentTime, outAcquireTime, outLatchTime,
+            outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGpuCompositionDoneTime, outDisplayPresentTime,
+            outDequeueReadyTime, outReleaseTime);
+}
+
+int Surface::dispatchGetWideColorSupport(va_list args) {
+    bool* outSupport = va_arg(args, bool*);
+    return getWideColorSupport(outSupport);
+}
+
+int Surface::dispatchGetHdrSupport(va_list args) {
+    bool* outSupport = va_arg(args, bool*);
+    return getHdrSupport(outSupport);
 }
 
 int Surface::connect(int api) {
@@ -819,23 +1141,28 @@
 }
 
 int Surface::connect(int api, const sp<IProducerListener>& listener) {
+    return connect(api, listener, false);
+}
+
+int Surface::connect(
+        int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
     ATRACE_CALL();
     ALOGV("Surface::connect");
     Mutex::Autolock lock(mMutex);
     IGraphicBufferProducer::QueueBufferOutput output;
+    mReportRemovedBuffers = reportBufferRemoval;
     int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
     if (err == NO_ERROR) {
-        uint32_t numPendingBuffers = 0;
-        uint32_t hint = 0;
-        output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
-                &numPendingBuffers, &mNextFrameNumber);
+        mDefaultWidth = output.width;
+        mDefaultHeight = output.height;
+        mNextFrameNumber = output.nextFrameNumber;
 
         // Disable transform hint if sticky transform is set.
         if (mStickyTransform == 0) {
-            mTransformHint = hint;
+            mTransformHint = output.transformHint;
         }
 
-        mConsumerRunningBehind = (numPendingBuffers >= 2);
+        mConsumerRunningBehind = (output.numPendingBuffers >= 2);
     }
     if (!err && api == NATIVE_WINDOW_API_CPU) {
         mConnectedToCpu = true;
@@ -854,6 +1181,7 @@
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
+    mRemovedBuffers.clear();
     mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
     mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
@@ -885,6 +1213,9 @@
     }
 
     Mutex::Autolock lock(mMutex);
+    if (mReportRemovedBuffers) {
+        mRemovedBuffers.clear();
+    }
 
     sp<GraphicBuffer> buffer(NULL);
     sp<Fence> fence(NULL);
@@ -903,7 +1234,10 @@
 
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         if (mSlots[i].buffer != NULL &&
-                mSlots[i].buffer->handle == buffer->handle) {
+                mSlots[i].buffer->getId() == buffer->getId()) {
+            if (mReportRemovedBuffers) {
+                mRemovedBuffers.push_back(mSlots[i].buffer);
+            }
             mSlots[i].buffer = NULL;
         }
     }
@@ -917,6 +1251,9 @@
     ALOGV("Surface::attachBuffer");
 
     Mutex::Autolock lock(mMutex);
+    if (mReportRemovedBuffers) {
+        mRemovedBuffers.clear();
+    }
 
     sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
     uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
@@ -929,6 +1266,9 @@
         graphicBuffer->mGenerationNumber = priorGeneration;
         return result;
     }
+    if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) {
+        mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
+    }
     mSlots[attachedSlot].buffer = graphicBuffer;
 
     return NO_ERROR;
@@ -1178,7 +1518,8 @@
 static status_t copyBlt(
         const sp<GraphicBuffer>& dst,
         const sp<GraphicBuffer>& src,
-        const Region& reg)
+        const Region& reg,
+        int *dstFenceFd)
 {
     // src and dst with, height and format must be identical. no verification
     // is done here.
@@ -1189,9 +1530,10 @@
     ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
 
     uint8_t* dst_bits = NULL;
-    err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
-            reinterpret_cast<void**>(&dst_bits));
+    err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
+            reinterpret_cast<void**>(&dst_bits), *dstFenceFd);
     ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+    *dstFenceFd = -1;
 
     Region::const_iterator head(reg.begin());
     Region::const_iterator tail(reg.end());
@@ -1225,7 +1567,7 @@
         src->unlock();
 
     if (dst_bits)
-        dst->unlock();
+        dst->unlockAsync(dstFenceFd);
 
     return err;
 }
@@ -1275,8 +1617,9 @@
         if (canCopyBack) {
             // copy the area that is invalid and not repainted this round
             const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
-            if (!copyback.isEmpty())
-                copyBlt(backBuffer, frontBuffer, copyback);
+            if (!copyback.isEmpty()) {
+                copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
+            }
         } else {
             // if we can't copy-back anything, modify the user's dirty
             // region to make sure they redraw the whole buffer
@@ -1359,70 +1702,21 @@
     return mGraphicBufferProducer->getUniqueId(outId);
 }
 
-namespace view {
-
-status_t Surface::writeToParcel(Parcel* parcel) const {
-    return writeToParcel(parcel, false);
+nsecs_t Surface::getLastDequeueStartTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mLastDequeueStartTime;
 }
 
-status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
-    if (parcel == nullptr) return BAD_VALUE;
-
-    status_t res = OK;
-
-    if (!nameAlreadyWritten) {
-        res = parcel->writeString16(name);
-        if (res != OK) return res;
-
-        /* isSingleBuffered defaults to no */
-        res = parcel->writeInt32(0);
-        if (res != OK) return res;
+status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
+    if (out == nullptr) {
+        ALOGE("%s: out must not be null!", __FUNCTION__);
+        return BAD_VALUE;
     }
 
-    res = parcel->writeStrongBinder(
-            IGraphicBufferProducer::asBinder(graphicBufferProducer));
-
-    return res;
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel) {
-    return readFromParcel(parcel, false);
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
-    if (parcel == nullptr) return BAD_VALUE;
-
-    status_t res = OK;
-    if (!nameAlreadyRead) {
-        name = readMaybeEmptyString16(parcel);
-        // Discard this for now
-        int isSingleBuffered;
-        res = parcel->readInt32(&isSingleBuffered);
-        if (res != OK) {
-            return res;
-        }
-    }
-
-    sp<IBinder> binder;
-
-    res = parcel->readStrongBinder(&binder);
-    if (res != OK) return res;
-
-    graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
-
+    Mutex::Autolock lock(mMutex);
+    *out = mRemovedBuffers;
+    mRemovedBuffers.clear();
     return OK;
 }
 
-String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
-    size_t len;
-    const char16_t* str = parcel->readString16Inplace(&len);
-    if (str != nullptr) {
-        return String16(str, len);
-    } else {
-        return String16();
-    }
-}
-
-} // namespace view
-
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b78de2e..8c83843 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -26,17 +26,18 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
-#include <binder/IMemory.h>
 #include <binder/IServiceManager.h>
 
 #include <system/graphics.h>
 
 #include <ui/DisplayInfo.h>
 
+#include <gui/BufferItemConsumer.h>
 #include <gui/CpuConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
 #include <private/gui/ComposerService.h>
@@ -69,7 +70,7 @@
             mComposerService.composerServiceDied();
         }
      public:
-        DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
+        explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
     };
 
     mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
@@ -129,6 +130,8 @@
     void openGlobalTransactionImpl();
     void closeGlobalTransactionImpl(bool synchronous);
     void setAnimationTransactionImpl();
+    status_t enableVSyncInjectionsImpl(bool enable);
+    status_t injectVSyncImpl(nsecs_t when);
 
     layer_state_t* getLayerStateLocked(
             const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
@@ -145,7 +148,9 @@
     status_t setSize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             uint32_t w, uint32_t h);
     status_t setLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
-            uint32_t z);
+            int32_t z);
+    status_t setRelativeLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+            const sp<IBinder>& relativeTo, int32_t z);
     status_t setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             uint32_t flags, uint32_t mask);
     status_t setTransparentRegionHint(
@@ -154,7 +159,7 @@
     status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             float alpha);
     status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
-            float dsdx, float dtdx, float dsdy, float dtdy);
+            float dsdx, float dtdx, float dtdy, float dsdy);
     status_t setOrientation(int orientation);
     status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             const Rect& crop);
@@ -165,6 +170,14 @@
     status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, const sp<IBinder>& handle,
             uint64_t frameNumber);
+    status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id, const sp<Surface>& barrierSurface,
+            uint64_t frameNumber);
+    status_t reparentChildren(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
+    status_t detachChildren(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id);
     status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, int32_t overrideScalingMode);
     status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
@@ -190,6 +203,14 @@
     static void closeGlobalTransaction(bool synchronous) {
         Composer::getInstance().closeGlobalTransactionImpl(synchronous);
     }
+
+    static status_t enableVSyncInjections(bool enable) {
+        return Composer::getInstance().enableVSyncInjectionsImpl(enable);
+    }
+
+    static status_t injectVSync(nsecs_t when) {
+        return Composer::getInstance().injectVSyncImpl(when);
+    }
 };
 
 ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
@@ -253,6 +274,16 @@
    sm->setTransactionState(transaction, displayTransaction, flags);
 }
 
+status_t Composer::enableVSyncInjectionsImpl(bool enable) {
+    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+    return sm->enableVSyncInjections(enable);
+}
+
+status_t Composer::injectVSyncImpl(nsecs_t when) {
+    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+    return sm->injectVSync(when);
+}
+
 void Composer::setAnimationTransactionImpl() {
     Mutex::Autolock _l(mLock);
     mAnimation = true;
@@ -304,7 +335,7 @@
 }
 
 status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
-        const sp<IBinder>& id, uint32_t z) {
+        const sp<IBinder>& id, int32_t z) {
     Mutex::Autolock _l(mLock);
     layer_state_t* s = getLayerStateLocked(client, id);
     if (!s)
@@ -314,6 +345,20 @@
     return NO_ERROR;
 }
 
+status_t Composer::setRelativeLayer(const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id, const sp<IBinder>& relativeTo,
+        int32_t z) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eRelativeLayerChanged;
+    s->relativeLayerHandle = relativeTo;
+    s->z = z;
+    return NO_ERROR;
+}
+
 status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, uint32_t flags,
         uint32_t mask) {
@@ -368,7 +413,7 @@
 
 status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, float dsdx, float dtdx,
-        float dsdy, float dtdy) {
+        float dtdy, float dsdy) {
     Mutex::Autolock _l(mLock);
     layer_state_t* s = getLayerStateLocked(client, id);
     if (!s)
@@ -415,11 +460,51 @@
         return BAD_INDEX;
     }
     s->what |= layer_state_t::eDeferTransaction;
-    s->handle = handle;
+    s->barrierHandle = handle;
     s->frameNumber = frameNumber;
     return NO_ERROR;
 }
 
+status_t Composer::deferTransactionUntil(
+        const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+        const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eDeferTransaction;
+    s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
+    s->frameNumber = frameNumber;
+    return NO_ERROR;
+}
+
+status_t Composer::reparentChildren(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id,
+        const sp<IBinder>& newParentHandle) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eReparentChildren;
+    s->reparentHandle = newParentHandle;
+    return NO_ERROR;
+}
+
+status_t Composer::detachChildren(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eDetachChildren;
+    return NO_ERROR;
+}
+
 status_t Composer::setOverrideScalingMode(
         const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, int32_t overrideScalingMode) {
@@ -529,10 +614,18 @@
 {
 }
 
+SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
+    : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root)
+{
+}
+
 void SurfaceComposerClient::onFirstRef() {
     sp<ISurfaceComposer> sm(ComposerService::getComposerService());
     if (sm != 0) {
-        sp<ISurfaceComposerClient> conn = sm->createConnection();
+        auto rootProducer = mParent.promote();
+        sp<ISurfaceComposerClient> conn;
+        conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) :
+                sm->createConnection();
         if (conn != 0) {
             mClient = conn;
             mStatus = NO_ERROR;
@@ -575,14 +668,22 @@
         uint32_t w,
         uint32_t h,
         PixelFormat format,
-        uint32_t flags)
+        uint32_t flags,
+        SurfaceControl* parent,
+        uint32_t windowType,
+        uint32_t ownerUid)
 {
     sp<SurfaceControl> sur;
     if (mStatus == NO_ERROR) {
         sp<IBinder> handle;
+        sp<IBinder> parentHandle;
         sp<IGraphicBufferProducer> gbp;
-        status_t err = mClient->createSurface(name, w, h, format, flags,
-                &handle, &gbp);
+
+        if (parent != nullptr) {
+            parentHandle = parent->getHandle();
+        }
+        status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle,
+                windowType, ownerUid, &handle, &gbp);
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
             sur = new SurfaceControl(this, handle, gbp);
@@ -626,14 +727,6 @@
     return mClient->getLayerFrameStats(token, outStats);
 }
 
-status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
-        bool* outTransformToDisplayInverse) const {
-    if (mStatus != NO_ERROR) {
-        return mStatus;
-    }
-    return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
-}
-
 inline Composer& SurfaceComposerClient::getComposer() {
     return mComposer;
 }
@@ -652,6 +745,14 @@
     Composer::setAnimationTransaction();
 }
 
+status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
+    return Composer::enableVSyncInjections(enable);
+}
+
+status_t SurfaceComposerClient::injectVSync(nsecs_t when) {
+    return Composer::injectVSync(when);
+}
+
 // ----------------------------------------------------------------------------
 
 status_t SurfaceComposerClient::setCrop(const sp<IBinder>& id, const Rect& crop) {
@@ -671,10 +772,15 @@
     return getComposer().setSize(this, id, w, h);
 }
 
-status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, uint32_t z) {
+status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, int32_t z) {
     return getComposer().setLayer(this, id, z);
 }
 
+status_t SurfaceComposerClient::setRelativeLayer(const sp<IBinder>& id,
+        const sp<IBinder>& relativeTo, int32_t z) {
+    return getComposer().setRelativeLayer(this, id, relativeTo, z);
+}
+
 status_t SurfaceComposerClient::hide(const sp<IBinder>& id) {
     return getComposer().setFlags(this, id,
             layer_state_t::eLayerHidden,
@@ -706,8 +812,8 @@
 }
 
 status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx,
-        float dsdy, float dtdy) {
-    return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
+        float dtdy, float dsdy) {
+    return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy);
 }
 
 status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
@@ -715,6 +821,20 @@
     return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
 }
 
+status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
+        const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+    return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber);
+}
+
+status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id,
+        const sp<IBinder>& newParentHandle) {
+    return getComposer().reparentChildren(this, id, newParentHandle);
+}
+
+status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
+    return getComposer().detachChildren(this, id);
+}
+
 status_t SurfaceComposerClient::setOverrideScalingMode(
         const sp<IBinder>& id, int32_t overrideScalingMode) {
     return getComposer().setOverrideScalingMode(
@@ -824,13 +944,40 @@
         const sp<IBinder>& display,
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform) {
+        int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == NULL) return NO_INIT;
     return s->captureScreen(display, producer, sourceCrop,
             reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
 }
 
+status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+        int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
+        uint32_t rotation,
+        sp<GraphicBuffer>* outBuffer) {
+    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    if (s == NULL) return NO_INIT;
+
+    sp<IGraphicBufferConsumer> gbpConsumer;
+    sp<IGraphicBufferProducer> producer;
+    BufferQueue::createBufferQueue(&producer, &gbpConsumer);
+    sp<BufferItemConsumer> consumer(new BufferItemConsumer(gbpConsumer,
+           GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER,
+           1, true));
+
+    status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight,
+            minLayerZ, maxLayerZ, useIdentityTransform,
+            static_cast<ISurfaceComposer::Rotation>(rotation));
+    if (ret != NO_ERROR) {
+        return ret;
+    }
+    BufferItem b;
+    consumer->acquireBuffer(&b, 0, true);
+    *outBuffer = b.mGraphicBuffer;
+    return ret;
+}
+
 ScreenshotClient::ScreenshotClient()
     : mHaveBuffer(false) {
     memset(&mBuffer, 0, sizeof(mBuffer));
@@ -852,7 +999,7 @@
 
 status_t ScreenshotClient::update(const sp<IBinder>& display,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform, uint32_t rotation) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == NULL) return NO_INIT;
@@ -879,7 +1026,7 @@
 
 status_t ScreenshotClient::update(const sp<IBinder>& display,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform) {
 
     return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
@@ -888,14 +1035,16 @@
 
 status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
         bool useIdentityTransform) {
-    return ScreenshotClient::update(display, sourceCrop, 0, 0, 0, -1U,
+    return ScreenshotClient::update(display, sourceCrop, 0, 0,
+            INT32_MIN, INT32_MAX,
             useIdentityTransform, ISurfaceComposer::eRotateNone);
 }
 
 status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
         uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) {
     return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
-            0, -1U, useIdentityTransform, ISurfaceComposer::eRotateNone);
+            INT32_MIN, INT32_MAX,
+            useIdentityTransform, ISurfaceComposer::eRotateNone);
 }
 
 void ScreenshotClient::release() {
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 33c1d90..58bd273 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -102,11 +102,19 @@
     if (err < 0) return err;
     return mClient->setLayerStack(mHandle, layerStack);
 }
-status_t SurfaceControl::setLayer(uint32_t layer) {
+
+status_t SurfaceControl::setLayer(int32_t layer) {
     status_t err = validate();
     if (err < 0) return err;
     return mClient->setLayer(mHandle, layer);
 }
+
+status_t SurfaceControl::setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->setRelativeLayer(mHandle, relativeTo, layer);
+}
+
 status_t SurfaceControl::setPosition(float x, float y) {
     status_t err = validate();
     if (err < 0) return err;
@@ -147,10 +155,10 @@
     if (err < 0) return err;
     return mClient->setAlpha(mHandle, alpha);
 }
-status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
     status_t err = validate();
     if (err < 0) return err;
-    return mClient->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy);
+    return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy);
 }
 status_t SurfaceControl::setCrop(const Rect& crop) {
     status_t err = validate();
@@ -163,13 +171,32 @@
     return mClient->setFinalCrop(mHandle, crop);
 }
 
-status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle,
+status_t SurfaceControl::deferTransactionUntil(const sp<IBinder>& handle,
         uint64_t frameNumber) {
     status_t err = validate();
     if (err < 0) return err;
     return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
 }
 
+status_t SurfaceControl::deferTransactionUntil(const sp<Surface>& handle,
+        uint64_t frameNumber) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
+}
+
+status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->reparentChildren(mHandle, newParentHandle);
+}
+
+status_t SurfaceControl::detachChildren() {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->detachChildren(mHandle);
+}
+
 status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
     status_t err = validate();
     if (err < 0) return err;
@@ -190,13 +217,6 @@
     return client->getLayerFrameStats(mHandle, outStats);
 }
 
-status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
-    status_t err = validate();
-    if (err < 0) return err;
-    const sp<SurfaceComposerClient>& client(mClient);
-    return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
-}
-
 status_t SurfaceControl::validate() const
 {
     if (mHandle==0 || mClient==0) {
@@ -217,17 +237,30 @@
     return parcel->writeStrongBinder(IInterface::asBinder(bp));
 }
 
+sp<Surface> SurfaceControl::generateSurfaceLocked() const
+{
+    // This surface is always consumed by SurfaceFlinger, so the
+    // producerControlledByApp value doesn't matter; using false.
+    mSurfaceData = new Surface(mGraphicBufferProducer, false);
+
+    return mSurfaceData;
+}
+
 sp<Surface> SurfaceControl::getSurface() const
 {
     Mutex::Autolock _l(mLock);
     if (mSurfaceData == 0) {
-        // This surface is always consumed by SurfaceFlinger, so the
-        // producerControlledByApp value doesn't matter; using false.
-        mSurfaceData = new Surface(mGraphicBufferProducer, false);
+        return generateSurfaceLocked();
     }
     return mSurfaceData;
 }
 
+sp<Surface> SurfaceControl::createSurface() const
+{
+    Mutex::Autolock _l(mLock);
+    return generateSurfaceLocked();
+}
+
 sp<IBinder> SurfaceControl::getHandle() const
 {
     Mutex::Autolock lock(mLock);
diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
new file mode 100644
index 0000000..a5f28cd
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(
+        sp<BProducerListener> const& base):
+    mBase(base) {
+}
+
+Return<void> B2HProducerListener::onBufferReleased() {
+    mBase->onBufferReleased();
+    return Void();
+}
+
+Return<bool> B2HProducerListener::needsReleaseNotify() {
+    return mBase->needsReleaseNotify();
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000..fda5b94
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,1234 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "H2BGraphicBufferProducer"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using Status = HGraphicBufferProducer::Status;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+typedef ::android::hardware::media::V1_0::Rect HRect;
+typedef ::android::hardware::media::V1_0::Region HRegion;
+
+// Conversion functions
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+    if (fd < 0) {
+        return native_handle_create(0, 0);
+    }
+    native_handle_t* nh = native_handle_create(1, 0);
+    if (nh == nullptr) {
+        return nullptr;
+    }
+    nh->data[0] = fd;
+    return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+    return ((nh == nullptr) || (nh->numFds == 0) ||
+            (nh->numFds <= index) || (index < 0)) ?
+            -1 : nh->data[index];
+}
+
+/**
+ * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
+ * calls.
+ *
+ * \param[in] t The source `Return<Status>`.
+ * \return The corresponding `status_t`.
+ *
+ * This function first check if \p t has a transport error. If it does, then the
+ * return value is the transport error code. Otherwise, the return value is
+ * converted from `Status` contained inside \p t.
+ *
+ * Note:
+ * - This `Status` is omx-specific. It is defined in `types.hal`.
+ * - The name of this function is not `convert`.
+ */
+// convert: Return<Status> -> status_t
+inline status_t toStatusT(Return<Status> const& t) {
+    return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+    return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+    t->attr.width = l.getWidth();
+    t->attr.height = l.getHeight();
+    t->attr.stride = l.getStride();
+    t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+    t->attr.layerCount = l.getLayerCount();
+    t->attr.usage = l.getUsage();
+    t->attr.id = l.getId();
+    t->attr.generationNumber = l.getGenerationNumber();
+    t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+    native_handle_t* handle = t.nativeHandle == nullptr ?
+            nullptr : native_handle_clone(t.nativeHandle);
+
+    size_t const numInts = 12 +
+            static_cast<size_t>(handle ? handle->numInts : 0);
+    int32_t* ints = new int32_t[numInts];
+
+    size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+    int* fds = new int[numFds];
+
+    ints[0] = 'GBFR';
+    ints[1] = static_cast<int32_t>(t.attr.width);
+    ints[2] = static_cast<int32_t>(t.attr.height);
+    ints[3] = static_cast<int32_t>(t.attr.stride);
+    ints[4] = static_cast<int32_t>(t.attr.format);
+    ints[5] = static_cast<int32_t>(t.attr.layerCount);
+    ints[6] = static_cast<int32_t>(t.attr.usage);
+    ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+    ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+    ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+    ints[10] = 0;
+    ints[11] = 0;
+    if (handle) {
+        ints[10] = static_cast<int32_t>(handle->numFds);
+        ints[11] = static_cast<int32_t>(handle->numInts);
+        int* intsStart = handle->data + handle->numFds;
+        std::copy(handle->data, intsStart, fds);
+        std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+    }
+
+    void const* constBuffer = static_cast<void const*>(ints);
+    size_t size = numInts * sizeof(int32_t);
+    int const* constFds = static_cast<int const*>(fds);
+    status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+    delete [] fds;
+    delete [] ints;
+    native_handle_delete(handle);
+    return status == NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+    return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+    return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < 4) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numFdsInHandle;
+    FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+    if (numFdsInHandle > 1) {
+        return BAD_VALUE;
+    }
+
+    if (numFds < numFdsInHandle) {
+        return NO_MEMORY;
+    }
+
+    if (numFdsInHandle) {
+        *nh = native_handle_create_from_fd(*fds);
+        if (*nh == nullptr) {
+            return NO_MEMORY;
+        }
+        *fence = *nh;
+        ++fds;
+        --numFds;
+    } else {
+        *nh = nullptr;
+        *fence = hidl_handle();
+    }
+
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] fence The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFenceFlattenedSize(fence) ||
+            numFds < getFenceFdCount(fence)) {
+        return NO_MEMORY;
+    }
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size,
+            static_cast<uint32_t>(getFenceFdCount(fence)));
+    int fd = native_handle_read_fd(fence);
+    if (fd != -1) {
+        *fds = fd;
+        ++fds;
+        --numFds;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+            != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+    int fd = native_handle_read_fd(t);
+    if (fd != -1) {
+        fd = dup(fd);
+        if (fd == -1) {
+            return false;
+        }
+    }
+    native_handle_t* nh = native_handle_create_from_fd(fd);
+    if (nh == nullptr) {
+        if (fd != -1) {
+            close(fd);
+        }
+        return false;
+    }
+
+    size_t const baseSize = getFenceFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    size_t const baseNumFds = getFenceFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+        native_handle_delete(nh);
+        return false;
+    }
+    native_handle_delete(nh);
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Unflatten `HRegion`.
+ *
+ * \param[out] t The destination `HRegion`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) {
+    if (size < sizeof(uint32_t)) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numRects = 0;
+    FlattenableUtils::read(buffer, size, numRects);
+    if (size < numRects * sizeof(HRect)) {
+        return NO_MEMORY;
+    }
+    if (numRects > (UINT32_MAX / sizeof(HRect))) {
+        return NO_MEMORY;
+    }
+
+    t->resize(numRects);
+    for (size_t r = 0; r < numRects; ++r) {
+        ::android::Rect rect(::android::Rect::EMPTY_RECT);
+        status_t status = rect.unflatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+        (*t)[r] = HRect{
+                static_cast<int32_t>(rect.left),
+                static_cast<int32_t>(rect.top),
+                static_cast<int32_t>(rect.right),
+                static_cast<int32_t>(rect.bottom)};
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+    return sizeof(int64_t) + // timestamp
+            sizeof(int) + // isAutoTimestamp
+            sizeof(android_dataspace) + // dataSpace
+            sizeof(::android::Rect) + // crop
+            sizeof(int) + // scalingMode
+            sizeof(uint32_t) + // transform
+            sizeof(uint32_t) + // stickyTransform
+            sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < minFlattenedSize(*t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, t->timestamp);
+    int lIsAutoTimestamp;
+    FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+    t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+    android_dataspace_t lDataSpace;
+    FlattenableUtils::read(buffer, size, lDataSpace);
+    t->dataSpace = static_cast<Dataspace>(lDataSpace);
+    ::android::Rect lCrop;
+    FlattenableUtils::read(buffer, size, lCrop);
+    t->crop = HRect{
+            static_cast<int32_t>(lCrop.left),
+            static_cast<int32_t>(lCrop.top),
+            static_cast<int32_t>(lCrop.right),
+            static_cast<int32_t>(lCrop.bottom)};
+    int lScalingMode;
+    FlattenableUtils::read(buffer, size, lScalingMode);
+    t->scalingMode = static_cast<int32_t>(lScalingMode);
+    FlattenableUtils::read(buffer, size, t->transform);
+    FlattenableUtils::read(buffer, size, t->stickyTransform);
+    FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+    status_t status = unflattenFence(&(t->fence), nh,
+            buffer, size, fds, numFds);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+        HGraphicBufferProducer::QueueBufferInput* t,
+        native_handle_t** nh,
+        BGraphicBufferProducer::QueueBufferInput const& l) {
+
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    constexpr size_t min = sizeof(t.state);
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            return min;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            return min + getFenceFlattenedSize(t.fence);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            return min + sizeof(
+                    ::android::FenceTime::Snapshot::signalTime);
+    }
+    return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    return t.state ==
+            HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+            getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[out] nh The cloned native handle, if necessary.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`, in which case \p nh will be returned.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+        native_handle_t** nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    *nh = nullptr;
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::EMPTY);
+            return NO_ERROR;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::FENCE);
+            *nh = t.fence.getNativeHandle() == nullptr ?
+                    nullptr : native_handle_clone(t.fence);
+            return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+            FlattenableUtils::write(buffer, size, t.signalTimeNs);
+            return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+    return sizeof(uint64_t) + // mFrameNumber
+            sizeof(uint8_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddRetireCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(nsecs_t) + // mPostedTime
+            sizeof(nsecs_t) + // mRequestedPresentTime
+            sizeof(nsecs_t) + // mLatchTime
+            sizeof(nsecs_t) + // mFirstRefreshStartTime
+            sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return minFlattenedSize(t) +
+            getFlattenedSize(t.gpuCompositionDoneFence) +
+            getFlattenedSize(t.displayPresentFence) +
+            getFlattenedSize(t.displayRetireFence) +
+            getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return getFdCount(t.gpuCompositionDoneFence) +
+            getFdCount(t.displayPresentFence) +
+            getFdCount(t.displayRetireFence) +
+            getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[out] nh The array of native handles that are cloned.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. These native handles will
+ * need to be closed by the caller.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+//      FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+        std::vector<native_handle_t*>* nh,
+        void*& buffer, size_t& size, int*& fds, size_t numFds) {
+    // Check that t.index is within a valid range.
+    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+            || t.index > std::numeric_limits<uint8_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, t.frameNumber);
+
+    // These are static_cast to uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+    FlattenableUtils::write(buffer, size, t.postedTimeNs);
+    FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+    FlattenableUtils::write(buffer, size, t.latchTimeNs);
+    FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+    // Fences
+    HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+    tSnapshot[0] = &t.gpuCompositionDoneFence;
+    tSnapshot[1] = &t.displayPresentFence;
+    tSnapshot[2] = &t.displayRetireFence;
+    tSnapshot[3] = &t.releaseFence;
+    nh->resize(4);
+    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+        status_t status = flatten(
+                *(tSnapshot[snapshotIndex]),
+                &((*nh)[snapshotIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (snapshotIndex > 0) {
+                --snapshotIndex;
+                native_handle_close((*nh)[snapshotIndex]);
+                native_handle_delete((*nh)[snapshotIndex]);
+                (*nh)[snapshotIndex] = nullptr;
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t size = 4 + // mDeltas.size()
+            sizeof(t.compositorTiming);
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        size += getFlattenedSize(t.deltas[i]);
+    }
+    return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t numFds = 0;
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        numFds += getFdCount(t.deltas[i]);
+    }
+    return numFds;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[out] nh The array of arrays of cloned native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. Before making the call, \p
+ * nh should have enough space to store `n` pointers to arrays of native
+ * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have
+ * enough space to store `4` native handles.
+ */
+inline status_t flatten(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+    nh->resize(t.deltas.size());
+    for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+        status_t status = flatten(
+                t.deltas[deltaIndex], &((*nh)[deltaIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (deltaIndex > 0) {
+                --deltaIndex;
+                for (size_t snapshotIndex = 0;
+                        snapshotIndex < 4; ++snapshotIndex) {
+                    native_handle_close((*nh)[deltaIndex][snapshotIndex]);
+                    native_handle_delete((*nh)[deltaIndex][snapshotIndex]);
+                    (*nh)[deltaIndex][snapshotIndex] = nullptr;
+                }
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+        ::android::FrameEventHistoryDelta* l,
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = getFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    std::vector<std::vector<native_handle_t*> > nhAA;
+    if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        for (auto nhA : nhAA) {
+            for (auto nh : nhA) {
+                if (nh != nullptr) {
+                    native_handle_close(nh);
+                    native_handle_delete(nh);
+                }
+            }
+        }
+        return false;
+    }
+
+    for (auto nhA : nhAA) {
+        for (auto nh : nhA) {
+            if (nh != nullptr) {
+                native_handle_delete(nh);
+            }
+        }
+    }
+    return true;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `IGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// IGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+        BGraphicBufferProducer::QueueBufferOutput* l,
+        HGraphicBufferProducer::QueueBufferOutput const& t) {
+    if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+        return false;
+    }
+    l->width = t.width;
+    l->height = t.height;
+    l->transformHint = t.transformHint;
+    l->numPendingBuffers = t.numPendingBuffers;
+    l->nextFrameNumber = t.nextFrameNumber;
+    l->bufferReplaced = t.bufferReplaced;
+    return true;
+}
+
+/**
+ * \brief Convert `IGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode(
+        BGraphicBufferProducer::DisconnectMode l) {
+    switch (l) {
+        case BGraphicBufferProducer::DisconnectMode::Api:
+            return HGraphicBufferProducer::DisconnectMode::API;
+        case BGraphicBufferProducer::DisconnectMode::AllLocal:
+            return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+    }
+    return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+// H2BGraphicBufferProducer
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    *buf = new GraphicBuffer();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->requestBuffer(
+            static_cast<int32_t>(slot),
+            [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(buf->get(), buffer)) {
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+        int maxDequeuedBuffers) {
+    return toStatusT(mBase->setMaxDequeuedBufferCount(
+            static_cast<int32_t>(maxDequeuedBuffers)));
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+    return toStatusT(mBase->setAsyncMode(async));
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+        int* slot, sp<Fence>* fence,
+        uint32_t w, uint32_t h, ::android::PixelFormat format,
+        uint32_t usage, FrameEventHistoryDelta* outTimestamps) {
+    *fence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->dequeueBuffer(
+            w, h, static_cast<PixelFormat>(format), usage,
+            outTimestamps != nullptr,
+            [&fnStatus, slot, fence, outTimestamps] (
+                    Status status,
+                    int32_t tSlot,
+                    hidl_handle const& tFence,
+                    HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+                fnStatus = toStatusT(status);
+                *slot = tSlot;
+                if (!convertTo(fence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (outTimestamps && !convertTo(outTimestamps, tTs)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output timestamps");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+    return toStatusT(mBase->detachBuffer(static_cast<int>(slot)));
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+        sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    *outBuffer = new GraphicBuffer();
+    *outFence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->detachNextBuffer(
+            [&fnStatus, outBuffer, outFence] (
+                    Status status,
+                    AnwBuffer const& tBuffer,
+                    hidl_handle const& tFence) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(outFence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (!convertTo(outBuffer->get(), tBuffer)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+        int* outSlot, const sp<GraphicBuffer>& buffer) {
+    AnwBuffer tBuffer;
+    wrapAs(&tBuffer, *buffer);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer,
+            [&fnStatus, outSlot] (Status status, int32_t slot) {
+                fnStatus = toStatusT(status);
+                *outSlot = slot;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+        int slot,
+        const QueueBufferInput& input,
+        QueueBufferOutput* output) {
+    HGraphicBufferProducer::QueueBufferInput tInput;
+    native_handle_t* nh;
+    if (!wrapAs(&tInput, &nh, input)) {
+        ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                "Invalid input");
+        return BAD_VALUE;
+    }
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    native_handle_delete(nh);
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    hidl_handle tFence;
+    native_handle_t* nh = nullptr;
+    if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
+        ALOGE("H2BGraphicBufferProducer::cancelBuffer - "
+                "Invalid input fence");
+        return BAD_VALUE;
+    }
+
+    status_t status = toStatusT(mBase->cancelBuffer(
+            static_cast<int32_t>(slot), tFence));
+    native_handle_delete(nh);
+    return status;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+    int result;
+    status_t transStatus = toStatusT(mBase->query(
+            static_cast<int32_t>(what),
+            [&result, value] (int32_t tResult, int32_t tValue) {
+                result = static_cast<int>(tResult);
+                *value = static_cast<int>(tValue);
+            }));
+    return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
+status_t H2BGraphicBufferProducer::connect(
+        const sp<IProducerListener>& listener, int api,
+        bool producerControlledByApp, QueueBufferOutput* output) {
+    sp<HProducerListener> tListener = listener == nullptr ?
+            nullptr : new B2HProducerListener(listener);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->connect(
+            tListener, static_cast<int32_t>(api), producerControlledByApp,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::connect - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+    return toStatusT(mBase->disconnect(
+            static_cast<int32_t>(api), toHDisconnectMode(mode)));
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+        const sp<NativeHandle>& stream) {
+    return toStatusT(mBase->setSidebandStream(stream == nullptr ? nullptr : stream->handle()));
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height,
+        ::android::PixelFormat format, uint32_t usage) {
+    mBase->allocateBuffers(
+            width, height, static_cast<PixelFormat>(format), usage);
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+    return toStatusT(mBase->allowAllocation(allow));
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
+    return toStatusT(mBase->setGenerationNumber(generationNumber));
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+    String8 lName;
+    mBase->getConsumerName([&lName] (hidl_string const& name) {
+                lName = name.c_str();
+            });
+    return lName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+    return toStatusT(mBase->setSharedBufferMode(sharedBufferMode));
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+    return toStatusT(mBase->setAutoRefresh(autoRefresh));
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+    return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout)));
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+        sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence,
+        float outTransformMatrix[16]) {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getLastQueuedBuffer(
+            [&fnStatus, outBuffer, outFence, &outTransformMatrix] (
+                    Status status,
+                    AnwBuffer const& buffer,
+                    hidl_handle const& fence,
+                    hidl_array<float, 16> const& transformMatrix) {
+                fnStatus = toStatusT(status);
+                *outBuffer = new GraphicBuffer();
+                if (!convertTo(outBuffer->get(), buffer)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                *outFence = new Fence();
+                if (!convertTo(outFence->get(), fence)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                std::copy(transformMatrix.data(),
+                        transformMatrix.data() + 16,
+                        outTransformMatrix);
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+    mBase->getFrameTimestamps([outDelta] (
+            HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) {
+                convertTo(outDelta, tDelta);
+            });
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getUniqueId(
+            [&fnStatus, outId] (Status status, uint64_t id) {
+                fnStatus = toStatusT(status);
+                *outId = id;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h
new file mode 100644
index 0000000..13c0162
--- /dev/null
+++ b/libs/gui/include/private/gui/BitTube.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcelable.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Parcel;
+
+namespace gui {
+
+class BitTube : public Parcelable {
+public:
+    // creates an uninitialized BitTube (to unparcel into)
+    BitTube() = default;
+
+    // creates a BitTube with a a specified send and receive buffer size
+    explicit BitTube(size_t bufsize);
+
+    // creates a BitTube with a default (4KB) send buffer
+    struct DefaultSizeType {};
+    static constexpr DefaultSizeType DefaultSize{};
+    explicit BitTube(DefaultSizeType);
+
+    explicit BitTube(const Parcel& data);
+
+    virtual ~BitTube() = default;
+
+    // check state after construction
+    status_t initCheck() const;
+
+    // get receive file-descriptor
+    int getFd() const;
+
+    // get the send file-descriptor.
+    int getSendFd() const;
+
+    // moves the receive file descriptor out of this BitTube
+    base::unique_fd moveReceiveFd();
+
+    // resets this BitTube's receive file descriptor to receiveFd
+    void setReceiveFd(base::unique_fd&& receiveFd);
+
+    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+    template <typename T>
+    static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
+        return sendObjects(tube, events, count, sizeof(T));
+    }
+
+    // receive objects (sized blobs). If the receiving buffer isn't large enough, excess messages
+    // are silently discarded.
+    template <typename T>
+    static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
+        return recvObjects(tube, events, count, sizeof(T));
+    }
+
+    // implement the Parcelable protocol. Only parcels the receive file descriptor
+    status_t writeToParcel(Parcel* reply) const;
+    status_t readFromParcel(const Parcel* parcel);
+
+private:
+    void init(size_t rcvbuf, size_t sndbuf);
+
+    // send a message. The write is guaranteed to send the whole message or fail.
+    ssize_t write(void const* vaddr, size_t size);
+
+    // receive a message. the passed buffer must be at least as large as the write call used to send
+    // the message, excess data is silently discarded.
+    ssize_t read(void* vaddr, size_t size);
+
+    base::unique_fd mSendFd;
+    mutable base::unique_fd mReceiveFd;
+
+    static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
+
+    static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize);
+};
+
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
new file mode 100644
index 0000000..7efdb14
--- /dev/null
+++ b/libs/gui/tests/Android.bp
@@ -0,0 +1,46 @@
+// Build the unit tests,
+
+// Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// to integrate with auto-test framework.
+cc_test {
+    name: "libgui_test",
+
+    clang: true,
+
+    srcs: [
+        "BufferItemConsumer_test.cpp",
+        "BufferQueue_test.cpp",
+        "CpuConsumer_test.cpp",
+        "FillBuffer.cpp",
+        "GLTest.cpp",
+        "IGraphicBufferProducer_test.cpp",
+        "Malicious.cpp",
+        "MultiTextureConsumer_test.cpp",
+        "StreamSplitter_test.cpp",
+        "SurfaceTextureClient_test.cpp",
+        "SurfaceTextureFBO_test.cpp",
+        "SurfaceTextureGLThreadToGL_test.cpp",
+        "SurfaceTextureGLToGL_test.cpp",
+        "SurfaceTextureGL_test.cpp",
+        "SurfaceTextureMultiContextGL_test.cpp",
+        "Surface_test.cpp",
+        "TextureRenderer.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore-utils",
+        "liblog",
+        "libEGL",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libbinder",
+        "libcutils",
+        "libgui",
+        "libhidlbase",
+        "libhidltransport",
+        "libui",
+        "libutils",
+        "libnativewindow"
+    ],
+}
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
deleted file mode 100644
index 6ad9986..0000000
--- a/libs/gui/tests/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-# Build the unit tests,
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := libgui_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
-    BufferQueue_test.cpp \
-    CpuConsumer_test.cpp \
-    FillBuffer.cpp \
-    GLTest.cpp \
-    IGraphicBufferProducer_test.cpp \
-    MultiTextureConsumer_test.cpp \
-    SRGB_test.cpp \
-    StreamSplitter_test.cpp \
-    SurfaceTextureClient_test.cpp \
-    SurfaceTextureFBO_test.cpp \
-    SurfaceTextureGLThreadToGL_test.cpp \
-    SurfaceTextureGLToGL_test.cpp \
-    SurfaceTextureGL_test.cpp \
-    SurfaceTextureMultiContextGL_test.cpp \
-    Surface_test.cpp \
-    TextureRenderer.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-	libEGL \
-	libGLESv1_CM \
-	libGLESv2 \
-	libbinder \
-	libcutils \
-	libgui \
-	libsync \
-	libui \
-	libutils \
-
-# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
-# to integrate with auto-test framework.
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
new file mode 100644
index 0000000..d64e530
--- /dev/null
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferItemConsumer_test"
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+namespace android {
+
+static constexpr int kWidth = 100;
+static constexpr int kHeight = 100;
+static constexpr int kMaxLockedBuffers = 3;
+static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kFrameSleepUs = 30 * 1000;
+
+class BufferItemConsumerTest : public ::testing::Test {
+   protected:
+    struct BufferFreedListener
+        : public BufferItemConsumer::BufferFreedListener {
+        explicit BufferFreedListener(BufferItemConsumerTest* test)
+            : mTest(test) {}
+        void onBufferFreed(const wp<GraphicBuffer>& /* gBuffer */) override {
+            mTest->HandleBufferFreed();
+        }
+        BufferItemConsumerTest* mTest;
+    };
+
+    void SetUp() override {
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mBIC =
+            new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true);
+        String8 name("BufferItemConsumer_Under_Test");
+        mBIC->setName(name);
+        mBFL = new BufferFreedListener(this);
+        mBIC->setBufferFreedListener(mBFL);
+
+        sp<IProducerListener> producerListener = new DummyProducerListener();
+        IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+        ASSERT_EQ(NO_ERROR,
+                  mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
+                                     true, &bufferOutput));
+        ASSERT_EQ(NO_ERROR,
+                  mProducer->setMaxDequeuedBufferCount(kMaxLockedBuffers));
+    }
+
+    int GetFreedBufferCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mFreedBufferCount;
+    }
+
+    void HandleBufferFreed() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mFreedBufferCount++;
+        ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
+    }
+
+    void DequeueBuffer(int* outSlot) {
+        ASSERT_NE(outSlot, nullptr);
+
+        int slot;
+        sp<Fence> outFence;
+        status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth,
+                                                kHeight, 0, 0, nullptr);
+        ASSERT_GE(ret, 0);
+
+        ALOGV("dequeueBuffer: slot=%d", slot);
+        if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+            ret = mProducer->requestBuffer(slot, &mBuffers[slot]);
+            ASSERT_EQ(NO_ERROR, ret);
+        }
+        *outSlot = slot;
+    }
+
+    void QueueBuffer(int slot) {
+        ALOGV("enqueueBuffer: slot=%d", slot);
+        IGraphicBufferProducer::QueueBufferInput bufferInput(
+            0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+            NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+        IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+        status_t ret = mProducer->queueBuffer(slot, bufferInput, &bufferOutput);
+        ASSERT_EQ(NO_ERROR, ret);
+    }
+
+    void AcquireBuffer(int* outSlot) {
+        ASSERT_NE(outSlot, nullptr);
+        BufferItem buffer;
+        status_t ret = mBIC->acquireBuffer(&buffer, 0, false);
+        ASSERT_EQ(NO_ERROR, ret);
+
+        ALOGV("acquireBuffer: slot=%d", buffer.mSlot);
+        *outSlot = buffer.mSlot;
+    }
+
+    void ReleaseBuffer(int slot) {
+        ALOGV("releaseBuffer: slot=%d", slot);
+        BufferItem buffer;
+        buffer.mSlot = slot;
+        buffer.mGraphicBuffer = mBuffers[slot];
+        status_t ret = mBIC->releaseBuffer(buffer, Fence::NO_FENCE);
+        ASSERT_EQ(NO_ERROR, ret);
+    }
+
+
+    std::mutex mMutex;
+    int mFreedBufferCount{0};
+
+    sp<BufferItemConsumer> mBIC;
+    sp<BufferFreedListener> mBFL;
+    sp<IGraphicBufferProducer> mProducer;
+    sp<IGraphicBufferConsumer> mConsumer;
+    sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+// Test that detaching buffer from consumer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
+    int slot;
+    // Producer: generate a dummy buffer.
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+
+    ASSERT_EQ(0, GetFreedBufferCount());
+    // Consumer: acquire the buffer and then detach it.
+    AcquireBuffer(&slot);
+    status_t ret = mBIC->detachBuffer(slot);
+    ASSERT_EQ(NO_ERROR, ret);
+
+    // Sleep to give some time for callbacks to happen.
+    usleep(kFrameSleepUs);
+    ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that detaching buffer from producer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromProducer) {
+    int slot;
+    // Let buffer go through the cycle at least once.
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+    AcquireBuffer(&slot);
+    ReleaseBuffer(slot);
+
+    ASSERT_EQ(0, GetFreedBufferCount());
+
+    // Producer: generate the buffer again.
+    DequeueBuffer(&slot);
+
+    // Producer: detach the buffer.
+    status_t ret = mProducer->detachBuffer(slot);
+    ASSERT_EQ(NO_ERROR, ret);
+
+    // Sleep to give some time for callbacks to happen.
+    usleep(kFrameSleepUs);
+    ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that abandoning BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_AbandonBufferItemConsumer) {
+    int slot;
+    // Let buffer go through the cycle at least once.
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+    AcquireBuffer(&slot);
+    ReleaseBuffer(slot);
+
+    // Abandon the BufferItemConsumer.
+    mBIC->abandon();
+
+    // Sleep to give some time for callbacks to happen.
+    usleep(kFrameSleepUs);
+    ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that delete BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) {
+    int slot;
+    // Let buffer go through the cycle at least once.
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+    AcquireBuffer(&slot);
+    ReleaseBuffer(slot);
+
+    // Delete the BufferItemConsumer.
+    mBIC.clear();
+
+    // Sleep to give some time for callbacks to happen.
+    usleep(kFrameSleepUs);
+    ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+}  // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 8a9eeee..60c1277 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -98,7 +98,11 @@
 
 // XXX: Tests that fork a process to hold the BufferQueue must run before tests
 // that use a local BufferQueue, or else Binder will get unhappy
-TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {
+//
+// In one instance this was a crash in the createBufferQueue where the
+// binder call to create a buffer allocator apparently got garbage back.
+// See b/36592665.
+TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) {
     const String16 PRODUCER_NAME = String16("BQTestProducer");
     const String16 CONSUMER_NAME = String16("BQTestConsumer");
 
@@ -139,7 +143,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -183,7 +187,7 @@
     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));
+                    GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -191,7 +195,7 @@
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-                GRALLOC_USAGE_SW_READ_OFTEN));
+                GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
 
@@ -234,7 +238,7 @@
     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));
+                    GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -270,7 +274,7 @@
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
-            GRALLOC_USAGE_SW_READ_OFTEN));
+            GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -280,7 +284,7 @@
     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));
+                GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -330,7 +334,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -379,7 +383,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     IGraphicBufferProducer::QueueBufferInput input(0, false,
             HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -415,7 +419,7 @@
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataOut;
@@ -438,7 +442,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -487,13 +491,13 @@
     // 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));
+            0, GRALLOC_USAGE_SW_WRITE_OFTEN, 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));
+            GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 
     // Release the previous buffer back to the BufferQueue
     mProducer->cancelBuffer(slot, fence);
@@ -501,7 +505,7 @@
     // 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));
+            WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -518,7 +522,7 @@
     int slot;
     sp<Fence> fence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
 
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -561,7 +565,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Queue the buffer
@@ -575,7 +579,8 @@
     // 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -612,7 +617,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Queue the buffer
@@ -639,7 +644,8 @@
     // 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -678,7 +684,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
 
     // Enable shared buffer mode
@@ -695,7 +701,8 @@
     // 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, nullptr));
         ASSERT_EQ(sharedSlot, slot);
         ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
     }
@@ -730,7 +737,8 @@
     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);
+        auto result = mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, nullptr);
         if (i < 2) {
             ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
                     result);
@@ -757,7 +765,8 @@
     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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, nullptr));
         ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
         IGraphicBufferProducer::QueueBufferInput input(0ull, true,
                 HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -768,7 +777,8 @@
     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));
+    ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
+            &slot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_GE(systemTime() - startTime, TIMEOUT);
 
     // We're technically attaching the same buffer multiple times (since we
@@ -789,7 +799,7 @@
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> sourceFence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
     ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -812,7 +822,7 @@
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
     sp<Fence> fence;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     sp<GraphicBuffer> firstBuffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
 
@@ -824,7 +834,7 @@
     // Dequeue a second buffer
     slot = BufferQueue::INVALID_BUFFER_SLOT;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     sp<GraphicBuffer> secondBuffer;
     ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
 
@@ -876,7 +886,7 @@
     mProducer->setMaxDequeuedBufferCount(3);
     for (size_t i = 0; i < 3; ++i) {
         status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
-                0, 0, 0, 0);
+                0, 0, 0, 0, nullptr);
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
         ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
     }
@@ -889,7 +899,8 @@
     // 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, 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,
@@ -904,16 +915,17 @@
     // 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, 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));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, 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,
@@ -928,10 +940,11 @@
 
     // 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));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, 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));
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(
+                &slot, &fence, 0, 0, 0, 0, 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,
@@ -1012,7 +1025,7 @@
     mProducer->setMaxDequeuedBufferCount(4);
     for (size_t i = 0; i < 4; ++i) {
         status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
-                0, 0, 0, 0);
+                0, 0, 0, 0, nullptr);
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
         ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
     }
@@ -1023,14 +1036,14 @@
     // Get buffers in all states: dequeued, filled, acquired, free
 
     // Fill 3 buffers
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
     // Dequeue 1 buffer
-    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
 
     // Acquire and free 1 buffer
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1044,7 +1057,7 @@
 
     // Check no free buffers in dump
     String8 dumpString;
-    mConsumer->dump(dumpString, nullptr);
+    mConsumer->dumpState(String8{}, &dumpString);
 
     // Parse the dump to ensure that all buffer slots that are FREE also
     // have a null GraphicBuffer
@@ -1067,4 +1080,122 @@
     }
 }
 
+TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence = Fence::NO_FENCE;
+    sp<GraphicBuffer> buffer = nullptr;
+    IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+        HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    BufferItem item{};
+
+    // Preallocate, dequeue, request, and cancel 2 buffers so we don't get
+    // BUFFER_NEEDS_REALLOCATION below
+    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);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+        ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+    }
+    for (size_t i = 0; i < 2; ++i) {
+        ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+    }
+
+    // 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->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->queueBuffer(slot, input, &output));
+    ASSERT_EQ(true, output.bufferReplaced);
+}
+
+TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    sp<IProducerListener> dummyListener(new DummyProducerListener);
+    ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
+            true, &output));
+
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence = Fence::NO_FENCE;
+    sp<GraphicBuffer> buffer = nullptr;
+    IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+            HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+            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);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+    ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+    // Acquire and release the buffer. Upon acquiring, the buffer handle should
+    // be non-null since this is the first time we've acquired this slot.
+    BufferItem item;
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(slot, item.mSlot);
+    ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            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->queueBuffer(slot, input, &output));
+
+    // Acquire and release the buffer again. Upon acquiring, the buffer handle
+    // should be null since this is not the first time we've acquired this slot.
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(slot, item.mSlot);
+    ASSERT_EQ(nullptr, item.mGraphicBuffer.get());
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            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->queueBuffer(slot, input, &output));
+
+    // Disconnect the producer end. This should clear all of the slots and mark
+    // the buffer in the queue as stale.
+    ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+
+    // Acquire the buffer again. Upon acquiring, the buffer handle should not be
+    // null since the queued buffer should have been marked as stale, which
+    // should trigger the BufferQueue to resend the buffer handle.
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(slot, item.mSlot);
+    ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+}
+
+TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    sp<IProducerListener> dummyListener(new DummyProducerListener);
+    ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+    ASSERT_EQ(OK, mProducer->connect(
+            dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
+    ASSERT_EQ(BAD_VALUE, mProducer->connect(
+            dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+
+    ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
+    ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+    ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
 } // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 289cc74..5848c74 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -160,7 +160,7 @@
 };
 
 #define ASSERT_NO_ERROR(err, msg) \
-    ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err)
+    ASSERT_EQ(NO_ERROR, err) << (msg) << strerror(-(err))
 
 void checkPixel(const CpuConsumer::LockedBuffer &buf,
         uint32_t x, uint32_t y, uint32_t r, uint32_t g=0, uint32_t b=0) {
@@ -490,7 +490,7 @@
 
     ASSERT_TRUE(anb != NULL);
 
-    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     *stride = buf->getStride();
     uint8_t* img = NULL;
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/DummyConsumer.h
index 0511e16..502bdf9 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/DummyConsumer.h
@@ -19,9 +19,9 @@
 namespace android {
 
 struct DummyConsumer : public BnConsumerListener {
-    virtual void onFrameAvailable(const BufferItem& /* item */) {}
-    virtual void onBuffersReleased() {}
-    virtual void onSidebandStreamChanged() {}
+    void onFrameAvailable(const BufferItem& /* item */) override {}
+    void onBuffersReleased() override {}
+    void onSidebandStreamChanged() override {}
 };
 
 } // namespace android
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index 079962c..ccd674f 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -95,7 +95,7 @@
             &anb));
     ASSERT_TRUE(anb != NULL);
 
-    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     uint8_t* img = NULL;
     ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..aa071f6 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "IGraphicBufferProducer_test"
 //#define LOG_NDEBUG 0
 
+#include "DummyConsumer.h"
+
 #include <gtest/gtest.h>
 
 #include <utils/String8.h>
@@ -64,12 +66,6 @@
     const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
 }; // namespace anonymous
 
-struct DummyConsumer : public BnConsumerListener {
-    virtual void onFrameAvailable(const BufferItem& /* item */) {}
-    virtual void onBuffersReleased() {}
-    virtual void onSidebandStreamChanged() {}
-};
-
 class IGraphicBufferProducerTest : public ::testing::Test {
 protected:
 
@@ -196,7 +192,7 @@
     };
 
     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);
+        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
     }
 
     void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +206,7 @@
 
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
-                DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+                DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
 
         EXPECT_LE(0, *slot);
         EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +345,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     EXPECT_LE(0, dequeuedSlot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +362,12 @@
     ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
 
     {
-        uint32_t width;
-        uint32_t height;
-        uint32_t transformHint;
-        uint32_t numPendingBuffers;
-        uint64_t nextFrameNumber;
-
-        output.deflate(&width, &height, &transformHint, &numPendingBuffers,
-                &nextFrameNumber);
-
-        EXPECT_EQ(DEFAULT_WIDTH, width);
-        EXPECT_EQ(DEFAULT_HEIGHT, height);
-        EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
-        EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
-        EXPECT_EQ(2u, nextFrameNumber);
+        EXPECT_EQ(DEFAULT_WIDTH, output.width);
+        EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+        EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+        // Since queueBuffer was called exactly once
+        EXPECT_EQ(1u, output.numPendingBuffers);
+        EXPECT_EQ(2u, output.nextFrameNumber);
     }
 
     // Buffer was not in the dequeued state
@@ -416,7 +404,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     // Slot was enqueued without requesting a buffer
     {
@@ -485,7 +473,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     // No return code, but at least test that it doesn't blow up...
     // TODO: add a return code
@@ -534,7 +522,7 @@
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS)))
+                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
                 << "iteration: " << i << ", slot: " << dequeuedSlot;
     }
 
@@ -571,7 +559,7 @@
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS)))
+                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -606,7 +594,8 @@
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+                TEST_PRODUCER_USAGE_BITS, nullptr)))
+                << "slot : " << dequeuedSlot;
         ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
         ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
     }
@@ -622,7 +611,8 @@
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+                TEST_PRODUCER_USAGE_BITS, nullptr)))
+                << "slot: " << dequeuedSlot;
     }
 
     // Abandon buffer queue
@@ -639,7 +629,7 @@
     sp<Fence> fence;
 
     ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
 }
 
 TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +649,8 @@
 
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+            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
new file mode 100644
index 0000000..7ecf08c
--- /dev/null
+++ b/libs/gui/tests/Malicious.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#include <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+#include <android/native_window.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace test {
+
+class ProxyBQP : public BnGraphicBufferProducer {
+public:
+    ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {}
+
+    // Pass through calls to mProducer
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+        return mProducer->requestBuffer(slot, buf);
+    }
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+        return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+    }
+    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,
+                           uint32_t usage, FrameEventHistoryDelta* outTimestamps) override {
+        return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps);
+    }
+    status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); }
+    status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+        return mProducer->detachNextBuffer(outBuffer, outFence);
+    }
+    status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override {
+        return mProducer->attachBuffer(outSlot, buffer);
+    }
+    status_t queueBuffer(int slot, const QueueBufferInput& input,
+                         QueueBufferOutput* output) override {
+        return mProducer->queueBuffer(slot, input, output);
+    }
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+        return mProducer->cancelBuffer(slot, fence);
+    }
+    int query(int what, int* value) override { return mProducer->query(what, value); }
+    status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+                     QueueBufferOutput* output) override {
+        return mProducer->connect(listener, api, producerControlledByApp, output);
+    }
+    status_t disconnect(int api, DisconnectMode mode) override {
+        return mProducer->disconnect(api, mode);
+    }
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+        return mProducer->setSidebandStream(stream);
+    }
+    void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+                         uint32_t usage) override {
+        mProducer->allocateBuffers(width, height, format, usage);
+    }
+    status_t allowAllocation(bool allow) override { return mProducer->allowAllocation(allow); }
+    status_t setGenerationNumber(uint32_t generationNumber) override {
+        return mProducer->setGenerationNumber(generationNumber);
+    }
+    String8 getConsumerName() const override { return mProducer->getConsumerName(); }
+    status_t setSharedBufferMode(bool sharedBufferMode) override {
+        return mProducer->setSharedBufferMode(sharedBufferMode);
+    }
+    status_t setAutoRefresh(bool autoRefresh) override {
+        return mProducer->setAutoRefresh(autoRefresh);
+    }
+    status_t setDequeueTimeout(nsecs_t timeout) override {
+        return mProducer->setDequeueTimeout(timeout);
+    }
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                 float outTransformMatrix[16]) override {
+        return mProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix);
+    }
+    void getFrameTimestamps(FrameEventHistoryDelta*) override {}
+    status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); }
+
+protected:
+    sp<IGraphicBufferProducer> mProducer;
+};
+
+class MaliciousBQP : public ProxyBQP {
+public:
+    MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {}
+
+    void beMalicious(int32_t value) { mMaliciousValue = value; }
+
+    void setExpectedSlot(int32_t slot) { mExpectedSlot = slot; }
+
+    // Override dequeueBuffer, optionally corrupting the returned slot number
+    status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
+                           PixelFormat format, uint32_t usage,
+                           FrameEventHistoryDelta* outTimestamps) override {
+        EXPECT_EQ(BUFFER_NEEDS_REALLOCATION,
+                  mProducer->dequeueBuffer(buf, fence, width, height, format, usage,
+                                           outTimestamps));
+        EXPECT_EQ(mExpectedSlot, *buf);
+        if (mMaliciousValue != 0) {
+            *buf = mMaliciousValue;
+            return NO_ERROR;
+        } else {
+            return BUFFER_NEEDS_REALLOCATION;
+        }
+    }
+
+private:
+    int32_t mMaliciousValue = 0;
+    int32_t mExpectedSlot = 0;
+};
+
+class DummyListener : public BnConsumerListener {
+public:
+    void onFrameAvailable(const BufferItem&) override {}
+    void onBuffersReleased() override {}
+    void onSidebandStreamChanged() override {}
+};
+
+sp<MaliciousBQP> getMaliciousBQP() {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    sp<IConsumerListener> listener = new DummyListener;
+    consumer->consumerConnect(listener, false);
+
+    sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
+    return malicious;
+}
+
+TEST(Malicious, Bug36991414Max) {
+    sp<MaliciousBQP> malicious = getMaliciousBQP();
+    sp<Surface> surface = new Surface(malicious);
+
+    ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+    ANativeWindow_Buffer buffer;
+    ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+    ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+    malicious->setExpectedSlot(1);
+    malicious->beMalicious(std::numeric_limits<int32_t>::max());
+    ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414Min) {
+    sp<MaliciousBQP> malicious = getMaliciousBQP();
+    sp<Surface> surface = new Surface(malicious);
+
+    ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+    ANativeWindow_Buffer buffer;
+    ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+    ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+    malicious->setExpectedSlot(1);
+    malicious->beMalicious(std::numeric_limits<int32_t>::min());
+    ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NegativeOne) {
+    sp<MaliciousBQP> malicious = getMaliciousBQP();
+    sp<Surface> surface = new Surface(malicious);
+
+    ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+    ANativeWindow_Buffer buffer;
+    ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+    ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+    malicious->setExpectedSlot(1);
+    malicious->beMalicious(-1);
+    ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NumSlots) {
+    sp<MaliciousBQP> malicious = getMaliciousBQP();
+    sp<Surface> surface = new Surface(malicious);
+
+    ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+    ANativeWindow_Buffer buffer;
+    ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+    ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+    malicious->setExpectedSlot(1);
+    malicious->beMalicious(BufferQueueDefs::NUM_BUFFER_SLOTS);
+    ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
deleted file mode 100644
index 3b11b97..0000000
--- a/libs/gui/tests/SRGB_test.cpp
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SRGB_test"
-//#define LOG_NDEBUG 0
-
-// Ignore for this file because it flags every instance of
-// ASSERT_EQ(GL_NO_ERROR, glGetError());
-#pragma clang diagnostic ignored "-Wsign-compare"
-
-#include "GLTest.h"
-
-#include <math.h>
-
-#include <gui/CpuConsumer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-
-#include <android/native_window.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class SRGBTest : public ::testing::Test {
-protected:
-    // Class constants
-    enum {
-        DISPLAY_WIDTH = 512,
-        DISPLAY_HEIGHT = 512,
-        PIXEL_SIZE = 4, // bytes or components
-        DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE,
-        ALPHA_VALUE = 223, // should be in [0, 255]
-        TOLERANCE = 1,
-    };
-    static const char SHOW_DEBUG_STRING[];
-
-    SRGBTest() :
-            mInputSurface(), mCpuConsumer(), mLockedBuffer(),
-            mEglDisplay(EGL_NO_DISPLAY), mEglConfig(),
-            mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE),
-            mComposerClient(), mSurfaceControl(), mOutputSurface() {
-    }
-
-    virtual ~SRGBTest() {
-        if (mEglDisplay != EGL_NO_DISPLAY) {
-            if (mEglSurface != EGL_NO_SURFACE) {
-                eglDestroySurface(mEglDisplay, mEglSurface);
-            }
-            if (mEglContext != EGL_NO_CONTEXT) {
-                eglDestroyContext(mEglDisplay, mEglContext);
-            }
-            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                    EGL_NO_CONTEXT);
-            eglTerminate(mEglDisplay);
-        }
-    }
-
-    virtual void SetUp() {
-        sp<IGraphicBufferProducer> producer;
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&producer, &consumer);
-        ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(
-                DISPLAY_WIDTH, DISPLAY_HEIGHT));
-        mCpuConsumer = new CpuConsumer(consumer, 1);
-        String8 name("CpuConsumer_for_SRGBTest");
-        mCpuConsumer->setName(name);
-        mInputSurface = new Surface(producer);
-
-        ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get()));
-        ASSERT_NO_FATAL_FAILURE(createDebugSurface());
-    }
-
-    virtual void TearDown() {
-        ASSERT_NO_FATAL_FAILURE(copyToDebugSurface());
-        ASSERT_TRUE(mLockedBuffer.data != NULL);
-        ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-    }
-
-    static float linearToSRGB(float l) {
-        if (l <= 0.0031308f) {
-            return l * 12.92f;
-        } else {
-            return 1.055f * pow(l, (1 / 2.4f)) - 0.055f;
-        }
-    }
-
-    static float srgbToLinear(float s) {
-        if (s <= 0.04045) {
-            return s / 12.92f;
-        } else {
-            return pow(((s + 0.055f) / 1.055f), 2.4f);
-        }
-    }
-
-    static uint8_t srgbToLinear(uint8_t u) {
-        float f = u / 255.0f;
-        return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f);
-    }
-
-    void fillTexture(bool writeAsSRGB) {
-        uint8_t* textureData = new uint8_t[DISPLAY_SIZE];
-
-        for (int y = 0; y < DISPLAY_HEIGHT; ++y) {
-            for (int x = 0; x < DISPLAY_WIDTH; ++x) {
-                float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1);
-                realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha
-                if (writeAsSRGB) {
-                    realValue = linearToSRGB(realValue);
-                }
-
-                int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE;
-                for (int c = 0; c < 3; ++c) {
-                    uint8_t intValue = static_cast<uint8_t>(
-                            realValue * 255.0f + 0.5f);
-                    textureData[offset + c] = intValue;
-                }
-                textureData[offset + 3] = ALPHA_VALUE;
-            }
-        }
-
-        glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8,
-                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-                textureData);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        delete[] textureData;
-    }
-
-    void initShaders() {
-        static const char vertexSource[] =
-            "attribute vec4 vPosition;\n"
-            "varying vec2 texCoords;\n"
-            "void main() {\n"
-            "  texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
-            "  gl_Position = vPosition;\n"
-            "}\n";
-
-        static const char fragmentSource[] =
-            "precision mediump float;\n"
-            "uniform sampler2D texSampler;\n"
-            "varying vec2 texCoords;\n"
-            "void main() {\n"
-            "  gl_FragColor = texture2D(texSampler, texCoords);\n"
-            "}\n";
-
-        GLuint program;
-        {
-            SCOPED_TRACE("Creating shader program");
-            ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(
-                    vertexSource, fragmentSource, &program));
-        }
-
-        GLint positionHandle = glGetAttribLocation(program, "vPosition");
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        ASSERT_NE(-1, positionHandle);
-
-        GLint samplerHandle = glGetUniformLocation(program, "texSampler");
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        ASSERT_NE(-1, samplerHandle);
-
-        static const GLfloat vertices[] = {
-            -1.0f, 1.0f,
-            -1.0f, -1.0f,
-            1.0f, -1.0f,
-            1.0f, 1.0f,
-        };
-
-        glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glEnableVertexAttribArray(positionHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        glUseProgram(program);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glUniform1i(samplerHandle, 0);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        GLuint textureHandle;
-        glGenTextures(1, &textureHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glBindTexture(GL_TEXTURE_2D, textureHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-    }
-
-    void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width,
-            GLsizei height) {
-        ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB));
-        glViewport(x, y, width, height);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-    }
-
-    void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) {
-        ASSERT_EQ(mLockedBuffer.format, format);
-        ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH);
-        ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT);
-        ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace);
-    }
-
-    static bool withinTolerance(int a, int b) {
-        int diff = a - b;
-        return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE;
-    }
-
-    // Primary producer and consumer
-    sp<Surface> mInputSurface;
-    sp<CpuConsumer> mCpuConsumer;
-    CpuConsumer::LockedBuffer mLockedBuffer;
-
-    EGLDisplay mEglDisplay;
-    EGLConfig mEglConfig;
-    EGLContext mEglContext;
-    EGLSurface mEglSurface;
-
-    // Auxiliary display output
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mSurfaceControl;
-    sp<Surface> mOutputSurface;
-
-private:
-    void createEGLSurface(Surface* inputSurface) {
-        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
-        EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-        static const EGLint configAttribs[] = {
-            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
-            EGL_RED_SIZE, 8,
-            EGL_GREEN_SIZE, 8,
-            EGL_BLUE_SIZE, 8,
-            EGL_ALPHA_SIZE, 8,
-            EGL_NONE };
-
-        EGLint numConfigs = 0;
-        EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
-                &numConfigs));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_GT(numConfigs, 0);
-
-        static const EGLint contextAttribs[] = {
-            EGL_CONTEXT_CLIENT_VERSION, 3,
-            EGL_NONE } ;
-
-        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
-                contextAttribs);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
-        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
-                inputSurface, NULL);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    }
-
-    void createDebugSurface() {
-        if (getenv(SHOW_DEBUG_STRING) == NULL) return;
-
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        mSurfaceControl = mComposerClient->createSurface(
-                String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                PIXEL_FORMAT_RGBA_8888);
-
-        ASSERT_TRUE(mSurfaceControl != NULL);
-        ASSERT_TRUE(mSurfaceControl->isValid());
-
-        SurfaceComposerClient::openGlobalTransaction();
-        ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
-        ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
-        SurfaceComposerClient::closeGlobalTransaction();
-
-        ANativeWindow_Buffer outBuffer;
-        ARect inOutDirtyBounds;
-        mOutputSurface = mSurfaceControl->getSurface();
-        mOutputSurface->lock(&outBuffer, &inOutDirtyBounds);
-        uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
-        for (int y = 0; y < outBuffer.height; ++y) {
-            int rowOffset = y * outBuffer.stride; // pixels
-            for (int x = 0; x < outBuffer.width; ++x) {
-                int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-                for (int c = 0; c < PIXEL_SIZE; ++c) {
-                    int offset = colOffset + c;
-                    bytePointer[offset] = ((c + 1) * 56) - 1;
-                }
-            }
-        }
-        mOutputSurface->unlockAndPost();
-    }
-
-    void copyToDebugSurface() {
-        if (!mOutputSurface.get()) return;
-
-        size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride *
-                PIXEL_SIZE;
-
-        ANativeWindow_Buffer outBuffer;
-        ARect outBufferBounds;
-        mOutputSurface->lock(&outBuffer, &outBufferBounds);
-        ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
-        ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
-        ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
-
-        if (mLockedBuffer.format == outBuffer.format) {
-            memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
-        } else {
-            ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888);
-            ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB);
-            ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888);
-            uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
-            for (int y = 0; y < outBuffer.height; ++y) {
-                int rowOffset = y * outBuffer.stride; // pixels
-                for (int x = 0; x < outBuffer.width; ++x) {
-                    int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-
-                    // RGB are converted
-                    for (int c = 0; c < (PIXEL_SIZE - 1); ++c) {
-                        outPointer[colOffset + c] = srgbToLinear(
-                                mLockedBuffer.data[colOffset + c]);
-                    }
-
-                    // Alpha isn't converted
-                    outPointer[colOffset + 3] =
-                            mLockedBuffer.data[colOffset + 3];
-                }
-            }
-        }
-        mOutputSurface->unlockAndPost();
-
-        int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING));
-        sleep(sleepSeconds);
-    }
-};
-
-const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS";
-
-TEST_F(SRGBTest, GLRenderFromSRGBTexture) {
-    ASSERT_NO_FATAL_FAILURE(initShaders());
-
-    // The RGB texture is displayed in the top half
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
-    // The SRGB texture is displayed in the bottom half
-    ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
-    // Compare a pixel in the middle of each texture
-    int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride *
-            PIXEL_SIZE;
-    int midRGBOffset = midSRGBOffset * 3;
-    midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        int expectedValue = mLockedBuffer.data[midRGBOffset + c];
-        int actualValue = mLockedBuffer.data[midSRGBOffset + c];
-        ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
-    }
-
-    // mLockedBuffer is unlocked in TearDown so we can copy data from it to
-    // the debug surface if necessary
-}
-
-// XXX: Disabled since we don't currently expect this to work
-TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
-    ASSERT_NO_FATAL_FAILURE(initShaders());
-
-    // By default, the first buffer we write into will be RGB
-
-    // Render an RGB texture across the whole surface
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT));
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
-    // Save the values of the middle pixel for later comparison against SRGB
-    uint8_t values[PIXEL_SIZE] = {};
-    int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride *
-            PIXEL_SIZE;
-    middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        values[c] = mLockedBuffer.data[middleOffset + c];
-    }
-
-    // Unlock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-
-    // Switch to SRGB window surface
-#define EGL_GL_COLORSPACE_KHR      EGL_VG_COLORSPACE
-#define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB
-
-    static const int srgbAttribs[] = {
-        EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
-        EGL_NONE,
-    };
-
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
-            mInputSurface.get(), srgbAttribs);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Render the texture again
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT));
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-
-    // Make sure we actually got the SRGB buffer on the consumer side
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB));
-
-    // Verify that the stored value is the same, accounting for RGB/SRGB
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        // The alpha value should be equivalent before linear->SRGB
-        float rgbAsSRGB = (c == 3) ? values[c] / 255.0f :
-                linearToSRGB(values[c] / 255.0f);
-        int expectedValue = rgbAsSRGB * 255.0f + 0.5f;
-        int actualValue = mLockedBuffer.data[middleOffset + c];
-        ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
-    }
-
-    // mLockedBuffer is unlocked in TearDown so we can copy data from it to
-    // the debug surface if necessary
-}
-
-} // namespace android
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e..80e30da 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -115,7 +115,7 @@
     // 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));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -190,7 +190,7 @@
     // 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));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     // Abandon the output
@@ -230,7 +230,7 @@
 
     // Input should be abandoned
     ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-            GRALLOC_USAGE_SW_WRITE_OFTEN));
+            GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 } // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index a1578f6..bd598e4 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -23,6 +23,7 @@
 #include <gtest/gtest.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
+#include <gui/BufferQueue.h>
 #include <system/graphics.h>
 #include <utils/Log.h>
 #include <utils/Thread.h>
@@ -535,7 +536,7 @@
             return false;
         }
     public:
-        MyThread(const sp<GLConsumer>& mST)
+        explicit MyThread(const sp<GLConsumer>& mST)
             : mST(mST), mBufferRetired(false) {
             ctx = eglGetCurrentContext();
             sur = eglGetCurrentSurface(EGL_DRAW);
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index 0606839..0134273 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -41,7 +41,7 @@
             &anb));
     ASSERT_TRUE(anb != NULL);
 
-    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with green
     uint8_t* img = NULL;
@@ -65,7 +65,7 @@
                 &anb));
         ASSERT_TRUE(anb != NULL);
 
-        buf = new GraphicBuffer(anb, false);
+        buf = GraphicBuffer::from(anb);
 
         // Fill the buffer with red
         ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 5311c59..c6745d0 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -42,7 +42,7 @@
             &anb));
     ASSERT_TRUE(anb != NULL);
 
-    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with the a checkerboard pattern
     uint8_t* img = NULL;
@@ -92,7 +92,7 @@
             &anb));
     ASSERT_TRUE(anb != NULL);
 
-    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with the a checkerboard pattern
     uint8_t* img = NULL;
@@ -157,7 +157,7 @@
                 &anb));
         ASSERT_TRUE(anb != NULL);
 
-        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+        sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
         uint8_t* img = NULL;
         buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -238,7 +238,7 @@
                     return false;
                 }
 
-                sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+                sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
                 const int yuvTexOffsetY = 0;
                 int stride = buf->getStride();
@@ -437,7 +437,7 @@
 
     class ProducerThread : public Thread {
     public:
-        ProducerThread(const sp<ANativeWindow>& anw):
+        explicit ProducerThread(const sp<ANativeWindow>& anw):
                 mANW(anw) {
         }
 
@@ -620,7 +620,7 @@
 TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
     class ProducerThread : public Thread {
     public:
-        ProducerThread(const sp<ANativeWindow>& anw):
+        explicit ProducerThread(const sp<ANativeWindow>& anw):
                 mANW(anw),
                 mDequeueError(NO_ERROR) {
         }
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..e18af17 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,19 +18,38 @@
 
 #include <gtest/gtest.h>
 
-#include <binder/IMemory.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <gui/BufferItemConsumer.h>
+#include <private/gui/ComposerService.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 
-#include <private/gui/ComposerService.h>
-#include <binder/ProcessState.h>
+#include <limits>
+#include <thread>
 
 namespace android {
 
+using namespace std::chrono_literals;
+// retrieve wide-color and hdr settings from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool hasWideColorDisplay =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
 class SurfaceTest : public ::testing::Test {
 protected:
 
@@ -42,6 +61,8 @@
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
+        // TODO(brianderson): The following sometimes fails and is a source of
+        //   test flakiness.
         mSurfaceControl = mComposerClient->createSurface(
                 String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
 
@@ -77,6 +98,8 @@
 
 TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) {
     mSurfaceControl.clear();
+    // Wait for the async clean-up to complete.
+    std::this_thread::sleep_for(50ms);
 
     sp<ANativeWindow> anw(mSurface);
     int result = -123;
@@ -96,7 +119,8 @@
     BufferQueue::createBufferQueue(&producer, &consumer);
     sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    sp<IBinder> display(sf->getBuiltInDisplay(
+            ISurfaceComposer::eDisplayIdMain));
     ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
             64, 64, 0, 0x7fffffff, false));
 
@@ -140,6 +164,14 @@
     EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
 }
 
+TEST_F(SurfaceTest, LayerCountIsOne) {
+    sp<ANativeWindow> anw(mSurface);
+    int result = -123;
+    int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result);
+    EXPECT_EQ(NO_ERROR, err);
+    EXPECT_EQ(1, result);
+}
+
 TEST_F(SurfaceTest, QueryConsumerUsage) {
     const int TEST_USAGE_FLAGS =
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
@@ -232,6 +264,37 @@
     EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
 }
 
+TEST_F(SurfaceTest, GetWideColorSupport) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+    bool supported;
+    surface->getWideColorSupport(&supported);
+
+    // NOTE: This test assumes that device that supports
+    // wide-color (as indicated by BoardConfig) must also
+    // have a wide-color primary display.
+    // That assumption allows this test to cover devices
+    // that advertised a wide-color color mode without
+    // actually supporting wide-color to pass this test
+    // as well as the case of a device that does support
+    // wide-color (via BoardConfig) and has a wide-color
+    // primary display.
+    // NOT covered at this time is a device that supports
+    // wide color in the BoardConfig but does not support
+    // a wide-color color mode on the primary display.
+    ASSERT_EQ(hasWideColorDisplay, supported);
+}
+
 TEST_F(SurfaceTest, DynamicSetBufferCount) {
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
@@ -258,4 +321,1268 @@
     ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
 }
 
+TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    sp<DummyProducerListener> listener = new DummyProducerListener();
+    ASSERT_EQ(OK, surface->connect(
+            NATIVE_WINDOW_API_CPU,
+            /*listener*/listener,
+            /*reportBufferRemoval*/true));
+    const int BUFFER_COUNT = 4;
+    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+    sp<GraphicBuffer> detachedBuffer;
+    sp<Fence> outFence;
+    int fences[BUFFER_COUNT];
+    ANativeWindowBuffer* buffers[BUFFER_COUNT];
+    // Allocate buffers because detachNextBuffer requires allocated buffers
+    for (int i = 0; i < BUFFER_COUNT; i++) {
+        ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+    }
+    for (int i = 0; i < BUFFER_COUNT; i++) {
+        ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+    }
+
+    // Test detached buffer is correctly reported
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+    std::vector<sp<GraphicBuffer>> removedBuffers;
+    ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+    ASSERT_EQ(1u, removedBuffers.size());
+    ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+    // Test the list is flushed one getAndFlushRemovedBuffers returns
+    ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+    ASSERT_EQ(0u, removedBuffers.size());
+
+
+    // Test removed buffer list is cleanup after next dequeueBuffer call
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0]));
+    ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+    ASSERT_EQ(0u, removedBuffers.size());
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0]));
+
+    // Test removed buffer list is cleanup after next detachNextBuffer call
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+    ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+    ASSERT_EQ(1u, removedBuffers.size());
+    ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+
+    // Re-allocate buffers since all buffers are detached up to now
+    for (int i = 0; i < BUFFER_COUNT; i++) {
+        ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+    }
+    for (int i = 0; i < BUFFER_COUNT; i++) {
+        ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+    }
+
+    ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+    ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get()));
+    ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+    // Depends on which slot GraphicBufferProducer impl pick, the attach call might
+    // get 0 or 1 buffer removed.
+    ASSERT_LE(removedBuffers.size(), 1u);
 }
+
+TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
+    sp<ANativeWindow> anw(mSurface);
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+
+    ANativeWindowBuffer* buffer = nullptr;
+    int32_t fenceFd = -1;
+
+    nsecs_t before = systemTime(CLOCK_MONOTONIC);
+    anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
+    nsecs_t after = systemTime(CLOCK_MONOTONIC);
+
+    nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+    ASSERT_LE(before, lastDequeueTime);
+    ASSERT_GE(after, lastDequeueTime);
+}
+
+class FakeConsumer : public BnConsumerListener {
+public:
+    void onFrameAvailable(const BufferItem& /*item*/) override {}
+    void onBuffersReleased() override {}
+    void onSidebandStreamChanged() override {}
+
+    void addAndGetFrameTimestamps(
+            const NewFrameEventsEntry* newTimestamps,
+            FrameEventHistoryDelta* outDelta) override {
+        if (newTimestamps) {
+            if (mGetFrameTimestampsEnabled) {
+                EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) <<
+                        "Test should set mNewFrameEntryOverride before queuing "
+                        "a frame.";
+                EXPECT_EQ(newTimestamps->frameNumber,
+                        mNewFrameEntryOverride.frameNumber) <<
+                        "Test attempting to add NewFrameEntryOverride with "
+                        "incorrect frame number.";
+                mFrameEventHistory.addQueue(mNewFrameEntryOverride);
+                mNewFrameEntryOverride.frameNumber = 0;
+            }
+            mAddFrameTimestampsCount++;
+            mLastAddedFrameNumber = newTimestamps->frameNumber;
+        }
+        if (outDelta) {
+            mFrameEventHistory.getAndResetDelta(outDelta);
+            mGetFrameTimestampsCount++;
+        }
+        mAddAndGetFrameTimestampsCallCount++;
+    }
+
+    bool mGetFrameTimestampsEnabled = false;
+
+    ConsumerFrameEventHistory mFrameEventHistory;
+    int mAddAndGetFrameTimestampsCallCount = 0;
+    int mAddFrameTimestampsCount = 0;
+    int mGetFrameTimestampsCount = 0;
+    uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX;
+
+    NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
+};
+
+
+class FakeSurfaceComposer : public ISurfaceComposer{
+public:
+    ~FakeSurfaceComposer() override {}
+
+    void setSupportsPresent(bool supportsPresent) {
+        mSupportsPresent = supportsPresent;
+    }
+
+    sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+    sp<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& /* parent */) override {
+        return nullptr;
+    }
+    sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
+            override {
+        return nullptr;
+    }
+    sp<IBinder> createDisplay(const String8& /*displayName*/,
+            bool /*secure*/) override { return nullptr; }
+    void destroyDisplay(const sp<IBinder>& /*display */) override {}
+    sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+    void setTransactionState(const Vector<ComposerState>& /*state*/,
+            const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
+            override {}
+    void bootFinished() override {}
+    bool authenticateSurfaceTexture(
+            const sp<IGraphicBufferProducer>& /*surface*/) const override {
+        return false;
+    }
+
+    status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported)
+            const override {
+        *outSupported = {
+                FrameEvent::REQUESTED_PRESENT,
+                FrameEvent::ACQUIRE,
+                FrameEvent::LATCH,
+                FrameEvent::FIRST_REFRESH_START,
+                FrameEvent::LAST_REFRESH_START,
+                FrameEvent::GPU_COMPOSITION_DONE,
+                FrameEvent::DEQUEUE_READY,
+                FrameEvent::RELEASE
+        };
+        if (mSupportsPresent) {
+            outSupported->push_back(
+                        FrameEvent::DISPLAY_PRESENT);
+        }
+        return NO_ERROR;
+    }
+
+    void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
+    status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
+            Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+    status_t getDisplayStats(const sp<IBinder>& /*display*/,
+            DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
+    int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
+    status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
+            override {
+        return NO_ERROR;
+    }
+    status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
+            Vector<android_color_mode_t>* /*outColorModes*/) override {
+        return NO_ERROR;
+    }
+    android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/)
+            override {
+        return HAL_COLOR_MODE_NATIVE;
+    }
+    status_t setActiveColorMode(const sp<IBinder>& /*display*/,
+            android_color_mode_t /*colorMode*/) override { return NO_ERROR; }
+    status_t captureScreen(const sp<IBinder>& /*display*/,
+            const sp<IGraphicBufferProducer>& /*producer*/,
+            Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+            int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
+            bool /*useIdentityTransform*/,
+            Rotation /*rotation*/) override { return NO_ERROR; }
+    status_t clearAnimationFrameStats() override { return NO_ERROR; }
+    status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
+        return NO_ERROR;
+    }
+    status_t getHdrCapabilities(const sp<IBinder>& /*display*/,
+            HdrCapabilities* /*outCapabilities*/) const override {
+        return NO_ERROR;
+    }
+    status_t enableVSyncInjections(bool /*enable*/) override {
+        return NO_ERROR;
+    }
+    status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+
+protected:
+    IBinder* onAsBinder() override { return nullptr; }
+
+private:
+    bool mSupportsPresent{true};
+};
+
+class FakeProducerFrameEventHistory : public ProducerFrameEventHistory {
+public:
+    FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap)
+        : mFenceMap(fenceMap) {}
+
+    ~FakeProducerFrameEventHistory() {}
+
+    void updateAcquireFence(uint64_t frameNumber,
+            std::shared_ptr<FenceTime>&& acquire) override {
+        // Verify the acquire fence being added isn't the one from the consumer.
+        EXPECT_NE(mConsumerAcquireFence, acquire);
+        // Override the fence, so we can verify this was called by the
+        // producer after the frame is queued.
+        ProducerFrameEventHistory::updateAcquireFence(frameNumber,
+                std::shared_ptr<FenceTime>(mAcquireFenceOverride));
+    }
+
+    void setAcquireFenceOverride(
+            const std::shared_ptr<FenceTime>& acquireFenceOverride,
+            const std::shared_ptr<FenceTime>& consumerAcquireFence) {
+        mAcquireFenceOverride = acquireFenceOverride;
+        mConsumerAcquireFence = consumerAcquireFence;
+    }
+
+protected:
+    std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence)
+            const override {
+        return mFenceMap->createFenceTimeForTest(fence);
+    }
+
+    FenceToFenceTimeMap* mFenceMap{nullptr};
+
+    std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE};
+};
+
+
+class TestSurface : public Surface {
+public:
+    TestSurface(const sp<IGraphicBufferProducer>& bufferProducer,
+            FenceToFenceTimeMap* fenceMap)
+        : Surface(bufferProducer),
+          mFakeSurfaceComposer(new FakeSurfaceComposer) {
+        mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap);
+        mFrameEventHistory.reset(mFakeFrameEventHistory);
+    }
+
+    ~TestSurface() override {}
+
+    sp<ISurfaceComposer> composerService() const override {
+        return mFakeSurfaceComposer;
+    }
+
+    nsecs_t now() const override {
+        return mNow;
+    }
+
+    void setNow(nsecs_t now) {
+        mNow = now;
+    }
+
+public:
+    sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+    nsecs_t mNow = 0;
+
+    // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+    // but this raw pointer gives access to test functionality.
+    FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public ::testing::Test {
+protected:
+    struct FenceAndFenceTime {
+        explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
+           : mFence(new Fence),
+             mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+        sp<Fence> mFence { nullptr };
+        std::shared_ptr<FenceTime> mFenceTime { nullptr };
+    };
+
+    struct RefreshEvents {
+        RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
+          : mFenceMap(fenceMap),
+            kCompositorTiming(
+                {refreshStart, refreshStart + 1, refreshStart + 2 }),
+            kStartTime(refreshStart + 3),
+            kGpuCompositionDoneTime(refreshStart + 4),
+            kPresentTime(refreshStart + 5) {}
+
+        void signalPostCompositeFences() {
+            mFenceMap.signalAllForTest(
+                        mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+            mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+        }
+
+        FenceToFenceTimeMap& mFenceMap;
+
+        FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+        FenceAndFenceTime mPresent { mFenceMap };
+
+        const CompositorTiming kCompositorTiming;
+
+        const nsecs_t kStartTime;
+        const nsecs_t kGpuCompositionDoneTime;
+        const nsecs_t kPresentTime;
+    };
+
+    struct FrameEvents {
+        FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime)
+            : mFenceMap(fenceMap),
+              kPostedTime(frameStartTime + 100),
+              kRequestedPresentTime(frameStartTime + 200),
+              kProducerAcquireTime(frameStartTime + 300),
+              kConsumerAcquireTime(frameStartTime + 301),
+              kLatchTime(frameStartTime + 500),
+              kDequeueReadyTime(frameStartTime + 600),
+              kReleaseTime(frameStartTime + 700),
+              mRefreshes {
+                    { mFenceMap, frameStartTime + 410 },
+                    { mFenceMap, frameStartTime + 420 },
+                    { mFenceMap, frameStartTime + 430 } } {}
+
+        void signalQueueFences() {
+            mFenceMap.signalAllForTest(
+                        mAcquireConsumer.mFence, kConsumerAcquireTime);
+            mFenceMap.signalAllForTest(
+                        mAcquireProducer.mFence, kProducerAcquireTime);
+        }
+
+        void signalRefreshFences() {
+            for (auto& re : mRefreshes) {
+                re.signalPostCompositeFences();
+            }
+        }
+
+        void signalReleaseFences() {
+            mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime);
+        }
+
+        FenceToFenceTimeMap& mFenceMap;
+
+        FenceAndFenceTime mAcquireConsumer { mFenceMap };
+        FenceAndFenceTime mAcquireProducer { mFenceMap };
+        FenceAndFenceTime mRelease { mFenceMap };
+
+        const nsecs_t kPostedTime;
+        const nsecs_t kRequestedPresentTime;
+        const nsecs_t kProducerAcquireTime;
+        const nsecs_t kConsumerAcquireTime;
+        const nsecs_t kLatchTime;
+        const nsecs_t kDequeueReadyTime;
+        const nsecs_t kReleaseTime;
+
+        RefreshEvents mRefreshes[3];
+    };
+
+    GetFrameTimestampsTest() {}
+
+    virtual void SetUp() {
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mFakeConsumer = new FakeConsumer;
+        mCfeh = &mFakeConsumer->mFrameEventHistory;
+        mConsumer->consumerConnect(mFakeConsumer, false);
+        mConsumer->setConsumerName(String8("TestConsumer"));
+        mSurface = new TestSurface(mProducer, &mFenceMap);
+        mWindow = mSurface;
+
+        ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
+                NATIVE_WINDOW_API_CPU));
+        native_window_set_buffer_count(mWindow.get(), 4);
+    }
+
+    void disableFrameTimestamps() {
+        mFakeConsumer->mGetFrameTimestampsEnabled = false;
+        native_window_enable_frame_timestamps(mWindow.get(), 0);
+        mFrameTimestampsEnabled = false;
+    }
+
+    void enableFrameTimestamps() {
+        mFakeConsumer->mGetFrameTimestampsEnabled = true;
+        native_window_enable_frame_timestamps(mWindow.get(), 1);
+        mFrameTimestampsEnabled = true;
+    }
+
+    int getAllFrameTimestamps(uint64_t frameId) {
+        return native_window_get_frame_timestamps(mWindow.get(), frameId,
+                &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+                &outFirstRefreshStartTime, &outLastRefreshStartTime,
+                &outGpuCompositionDoneTime, &outDisplayPresentTime,
+                &outDequeueReadyTime, &outReleaseTime);
+    }
+
+    void resetTimestamps() {
+        outRequestedPresentTime = -1;
+        outAcquireTime = -1;
+        outLatchTime = -1;
+        outFirstRefreshStartTime = -1;
+        outLastRefreshStartTime = -1;
+        outGpuCompositionDoneTime = -1;
+        outDisplayPresentTime = -1;
+        outDequeueReadyTime = -1;
+        outReleaseTime = -1;
+    }
+
+    uint64_t getNextFrameId() {
+        uint64_t frameId = -1;
+        int status = native_window_get_next_frame_id(mWindow.get(), &frameId);
+        EXPECT_EQ(status, NO_ERROR);
+        return frameId;
+    }
+
+    void dequeueAndQueue(uint64_t frameIndex) {
+        int fence = -1;
+        ANativeWindowBuffer* buffer = nullptr;
+        ASSERT_EQ(NO_ERROR,
+                mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+
+        int oldAddFrameTimestampsCount =
+                mFakeConsumer->mAddFrameTimestampsCount;
+
+        FrameEvents* frame = &mFrames[frameIndex];
+        uint64_t frameNumber = frameIndex + 1;
+
+        NewFrameEventsEntry fe;
+        fe.frameNumber = frameNumber;
+        fe.postedTime = frame->kPostedTime;
+        fe.requestedPresentTime = frame->kRequestedPresentTime;
+        fe.acquireFence = frame->mAcquireConsumer.mFenceTime;
+        mFakeConsumer->mNewFrameEntryOverride = fe;
+
+        mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+                    frame->mAcquireProducer.mFenceTime,
+                    frame->mAcquireConsumer.mFenceTime);
+
+        ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+
+        EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber);
+
+        EXPECT_EQ(
+                oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0),
+                mFakeConsumer->mAddFrameTimestampsCount);
+    }
+
+    void addFrameEvents(
+            bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) {
+        FrameEvents* oldFrame =
+                (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame];
+        FrameEvents* newFrame = &mFrames[iNewFrame];
+
+        uint64_t nOldFrame = iOldFrame + 1;
+        uint64_t nNewFrame = iNewFrame + 1;
+
+        // Latch, Composite, and Release the frames in a plausible order.
+        // Note: The timestamps won't necessarily match the order, but
+        // that's okay for the purposes of this test.
+        std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE;
+
+        // Composite the previous frame one more time, which helps verify
+        // LastRefresh is updated properly.
+        if (oldFrame != nullptr) {
+            mCfeh->addPreComposition(nOldFrame,
+                                     oldFrame->mRefreshes[2].kStartTime);
+            gpuDoneFenceTime = gpuComposited ?
+                    oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime :
+                    FenceTime::NO_FENCE;
+            mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime,
+                    oldFrame->mRefreshes[2].mPresent.mFenceTime,
+                    oldFrame->mRefreshes[2].kCompositorTiming);
+        }
+
+        // Latch the new frame.
+        mCfeh->addLatch(nNewFrame, newFrame->kLatchTime);
+
+        mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime);
+        gpuDoneFenceTime = gpuComposited ?
+                newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
+                FenceTime::NO_FENCE;
+        // HWC2 releases the previous buffer after a new latch just before
+        // calling postComposition.
+        if (oldFrame != nullptr) {
+            mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
+                    std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
+        }
+        mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+                newFrame->mRefreshes[0].mPresent.mFenceTime,
+                newFrame->mRefreshes[0].kCompositorTiming);
+
+        mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime);
+        gpuDoneFenceTime = gpuComposited ?
+                newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime :
+                FenceTime::NO_FENCE;
+        mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+                newFrame->mRefreshes[1].mPresent.mFenceTime,
+                newFrame->mRefreshes[1].kCompositorTiming);
+    }
+
+    sp<IGraphicBufferProducer> mProducer;
+    sp<IGraphicBufferConsumer> mConsumer;
+    sp<FakeConsumer> mFakeConsumer;
+    ConsumerFrameEventHistory* mCfeh;
+    sp<TestSurface> mSurface;
+    sp<ANativeWindow> mWindow;
+
+    FenceToFenceTimeMap mFenceMap;
+
+    bool mFrameTimestampsEnabled = false;
+
+    int64_t outRequestedPresentTime = -1;
+    int64_t outAcquireTime = -1;
+    int64_t outLatchTime = -1;
+    int64_t outFirstRefreshStartTime = -1;
+    int64_t outLastRefreshStartTime = -1;
+    int64_t outGpuCompositionDoneTime = -1;
+    int64_t outDisplayPresentTime = -1;
+    int64_t outDequeueReadyTime = -1;
+    int64_t outReleaseTime = -1;
+
+    FrameEvents mFrames[3] {
+        { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } };
+};
+
+
+// This test verifies that the frame timestamps are not retrieved when not
+// explicitly enabled via native_window_enable_frame_timestamps.
+// We want to check this to make sure there's no overhead for users
+// that don't need the timestamp information.
+TEST_F(GetFrameTimestampsTest, DefaultDisabled) {
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    const uint64_t fId = getNextFrameId();
+
+    // Verify the producer doesn't get frame timestamps piggybacked on dequeue.
+    ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify the producer doesn't get frame timestamps piggybacked on queue.
+    // It is okay that frame timestamps are added in the consumer since it is
+    // still needed for SurfaceFlinger dumps.
+    ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+    EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify attempts to get frame timestamps fail.
+    int result = getAllFrameTimestamps(fId);
+    EXPECT_EQ(INVALID_OPERATION, result);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify compositor timing query fails.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(INVALID_OPERATION, result);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    // Verify the compositor timing query gets the initial compositor values
+    // after timststamps are enabled; even before the first frame is queued
+    // or dequeued.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+    const uint64_t fId1 = getNextFrameId();
+
+    // Verify getFrameTimestamps is piggybacked on dequeue.
+    ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount);
+
+    NewFrameEventsEntry f1;
+    f1.frameNumber = 1;
+    f1.postedTime = mFrames[0].kPostedTime;
+    f1.requestedPresentTime = mFrames[0].kRequestedPresentTime;
+    f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime;
+    mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+            mFrames[0].mAcquireProducer.mFenceTime,
+            mFrames[0].mAcquireConsumer.mFenceTime);
+    mFakeConsumer->mNewFrameEntryOverride = f1;
+    mFrames[0].signalQueueFences();
+
+    // Verify getFrameTimestamps is piggybacked on queue.
+    ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+    EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber);
+    EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify queries for timestamps that the producer doesn't know about
+    // triggers a call to see if the consumer has any new timestamps.
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+    bool displayPresentSupported = true;
+    mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+    // Verify supported bits are forwarded.
+    int supportsPresent = -1;
+    mWindow.get()->query(mWindow.get(),
+            NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+    EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) {
+    bool displayPresentSupported = false;
+    mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+    // Verify supported bits are forwarded.
+    int supportsPresent = -1;
+    mWindow.get()->query(mWindow.get(),
+            NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+    EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) {
+    nsecs_t phase = 4000;
+    nsecs_t interval = 1000;
+
+    // Timestamp in previous interval.
+    nsecs_t timestamp = 3500;
+    EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp in next interval.
+    timestamp = 4500;
+    EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp multiple intervals before.
+    timestamp = 2500;
+    EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp multiple intervals after.
+    timestamp = 6500;
+    EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp on previous interval.
+    timestamp = 3000;
+    EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp on next interval.
+    timestamp = 5000;
+    EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp equal to phase.
+    timestamp = 4000;
+    EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+}
+
+// int(big_timestamp / interval) < 0, which can cause a crash or invalid result
+// if the number of intervals elapsed is internally stored in an int.
+TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) {
+      nsecs_t phase = 0;
+      nsecs_t interval = 4000;
+      nsecs_t big_timestamp = 8635916564000;
+      int32_t intervals = big_timestamp / interval;
+
+      EXPECT_LT(intervals, 0);
+      EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+            big_timestamp, phase, interval));
+      EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+            big_timestamp, big_timestamp, interval));
+}
+
+// This verifies the compositor timing is updated by refresh events
+// and piggy backed on a queue, dequeue, and enabling of timestamps..
+TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    // We get the initial values before any frames are submitted.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+    // Still get the initial values because the frame events for frame 0
+    // didn't get a chance to piggyback on a queue or dequeue yet.
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    addFrameEvents(true, 0, 1);
+
+    // Now expect the composite values associated with frame 1.
+    mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+
+    dequeueAndQueue(2);
+    addFrameEvents(true, 1, 2);
+
+    // Now expect the composite values associated with frame 2.
+    mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+
+    // Re-enabling frame timestamps should get the latest values.
+    disableFrameTimestamps();
+    enableFrameTimestamps();
+
+    // Now expect the composite values associated with frame 3.
+    mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+}
+
+// This verifies the compositor deadline properly snaps to the the next
+// deadline based on the current time.
+TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+
+    // A "now" just before the deadline snaps to the deadline.
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    nsecs_t expectedDeadline = initialCompositorTiming.deadline;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+    // A "now" just after the deadline snaps properly.
+    mSurface->setNow(initialCompositorTiming.deadline + 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            initialCompositorTiming.deadline +initialCompositorTiming.interval;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    addFrameEvents(true, 0, 1);
+
+    // A "now" just after the next interval snaps properly.
+    mSurface->setNow(
+            mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+            mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+            mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    dequeueAndQueue(2);
+    addFrameEvents(true, 1, 2);
+
+    // A "now" over 1 interval before the deadline snaps properly.
+    mSurface->setNow(
+            mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[1].mRefreshes[1].kCompositorTiming.interval;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    // Re-enabling frame timestamps should get the latest values.
+    disableFrameTimestamps();
+    enableFrameTimestamps();
+
+    // A "now" over 2 intervals before the deadline snaps properly.
+    mSurface->setNow(
+            mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+}
+
+// This verifies the timestamps recorded in the consumer's
+// FrameTimestampsHistory are properly retrieved by the producer for the
+// correct frames.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+    enableFrameTimestamps();
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    mFrames[0].signalRefreshFences();
+    addFrameEvents(true, 0, 1);
+    mFrames[0].signalReleaseFences();
+    mFrames[1].signalRefreshFences();
+
+    // Verify timestamps are correct for frame 1.
+    resetTimestamps();
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+    // Verify timestamps are correct for frame 2.
+    resetTimestamps();
+    result = getAllFrameTimestamps(fId2);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+}
+
+// This test verifies the acquire fence recorded by the consumer is not sent
+// back to the producer and the producer saves its own fence.
+TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) {
+    enableFrameTimestamps();
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+
+    // Verify queue-related timestamps for f1 are available immediately in the
+    // producer without asking the consumer again, even before signaling the
+    // acquire fence.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime);
+
+    // Signal acquire fences. Verify a sync call still isn't necessary.
+    mFrames[0].signalQueueFences();
+
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+
+    // Dequeue and queue frame 2.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+
+    // Verify queue-related timestamps for f2 are available immediately in the
+    // producer without asking the consumer again, even before signaling the
+    // acquire fence.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime);
+
+    // Signal acquire fences. Verify a sync call still isn't necessary.
+    mFrames[1].signalQueueFences();
+
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+}
+
+TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) {
+    enableFrameTimestamps();
+
+    // Dequeue and queue frame 1.
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    mFrames[0].signalRefreshFences();
+    addFrameEvents(true, 0, 1);
+    mFrames[0].signalReleaseFences();
+    mFrames[1].signalRefreshFences();
+
+    // Verify a request for no timestamps doesn't result in a sync call.
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+            nullptr, nullptr);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that fences can signal and update timestamps producer
+// side without an additional sync call to the consumer.
+TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) {
+    enableFrameTimestamps();
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    addFrameEvents(true, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+    // Verify available timestamps are correct for frame 1 again, before any
+    // fence has been signaled.
+    // This time a sync call should not be necessary.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+    // Signal the fences for frame 1.
+    mFrames[0].signalRefreshFences();
+    mFrames[0].signalReleaseFences();
+
+    // Verify all timestamps are available without a sync call.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the frame wasn't GPU composited but has a refresh
+// event a sync call isn't made to get the GPU composite done time since it will
+// never exist.
+TEST_F(GetFrameTimestampsTest, NoGpuNoSync) {
+    enableFrameTimestamps();
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(false, NO_FRAME_INDEX, 0);
+    addFrameEvents(false, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+    // Signal the fences for frame 1.
+    mFrames[0].signalRefreshFences();
+    mFrames[0].signalReleaseFences();
+
+    // Verify all timestamps, except GPU composition, are available without a
+    // sync call.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the certain timestamps can't possibly exist for
+// the most recent frame, then a sync call is not done.
+TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) {
+    enableFrameTimestamps();
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(false, NO_FRAME_INDEX, 0);
+    addFrameEvents(false, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+    mFrames[0].signalRefreshFences();
+    mFrames[0].signalReleaseFences();
+    mFrames[1].signalRefreshFences();
+
+    // Verify querying for all timestmaps of f2 does not do a sync call. Even
+    // though the lastRefresh, dequeueReady, and release times aren't
+    // available, a sync call should not occur because it's not possible for f2
+    // to encounter the final value for those events until another frame is
+    // queued.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId2);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+    EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present times
+// when they aren't supported and that an error is returned.
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+    enableFrameTimestamps();
+    mSurface->mFakeSurfaceComposer->setSupportsPresent(false);
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+
+    // Verify a query for the Present times do not trigger a sync call if they
+    // are not supported.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+            &outDisplayPresentTime, nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(BAD_VALUE, result);
+    EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
new file mode 100644
index 0000000..5ed3d3b
--- /dev/null
+++ b/libs/gui/view/Surface.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Surface"
+
+#include <gui/view/Surface.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/Log.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace view {
+
+status_t Surface::writeToParcel(Parcel* parcel) const {
+    return writeToParcel(parcel, false);
+}
+
+status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
+    if (parcel == nullptr) return BAD_VALUE;
+
+    status_t res = OK;
+
+    if (!nameAlreadyWritten) {
+        res = parcel->writeString16(name);
+        if (res != OK) return res;
+
+        /* isSingleBuffered defaults to no */
+        res = parcel->writeInt32(0);
+        if (res != OK) return res;
+    }
+
+    res = parcel->writeStrongBinder(
+            IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
+    return res;
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel) {
+    return readFromParcel(parcel, false);
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
+    if (parcel == nullptr) return BAD_VALUE;
+
+    status_t res = OK;
+    if (!nameAlreadyRead) {
+        name = readMaybeEmptyString16(parcel);
+        // Discard this for now
+        int isSingleBuffered;
+        res = parcel->readInt32(&isSingleBuffered);
+        if (res != OK) {
+            ALOGE("Can't read isSingleBuffered");
+            return res;
+        }
+    }
+
+    sp<IBinder> binder;
+
+    res = parcel->readNullableStrongBinder(&binder);
+    if (res != OK) {
+        ALOGE("%s: Can't read strong binder", __FUNCTION__);
+        return res;
+    }
+
+    graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
+
+    return OK;
+}
+
+String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
+    size_t len;
+    const char16_t* str = parcel->readString16Inplace(&len);
+    if (str != nullptr) {
+        return String16(str, len);
+    } else {
+        return String16();
+    }
+}
+
+} // namespace view
+} // namespace android
diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp
new file mode 100644
index 0000000..ec9cbf8
--- /dev/null
+++ b/libs/hwc2on1adapter/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libhwc2on1adapter",
+    vendor: true,
+
+    clang: true,
+    cppflags: [
+        "-Weverything",
+        "-Wall",
+        "-Wunused",
+        "-Wunreachable-code",
+
+        // The static constructors and destructors in this library have not been noted to
+        // introduce significant overheads
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+
+        // We only care about compiling as C++14
+        "-Wno-c++98-compat-pedantic",
+
+        // android/sensors.h uses nested anonymous unions and anonymous structs
+        "-Wno-nested-anon-types",
+        "-Wno-gnu-anonymous-struct",
+
+        // Don't warn about struct padding
+        "-Wno-padded",
+
+        // hwcomposer2.h features switch covering all cases.
+        "-Wno-covered-switch-default",
+
+        // hwcomposer.h features zero size array.
+        "-Wno-zero-length-array",
+
+        // Disabling warning specific to hwc2on1adapter code
+        "-Wno-double-promotion",
+        "-Wno-sign-conversion",
+        "-Wno-switch-enum",
+        "-Wno-float-equal",
+        "-Wno-shorten-64-to-32",
+        "-Wno-sign-compare",
+        "-Wno-missing-prototypes",
+    ],
+
+    srcs: [
+        "HWC2On1Adapter.cpp",
+        "MiniFence.cpp",
+    ],
+
+    shared_libs: [
+        "libutils",
+        "libcutils",
+        "liblog",
+        "libhardware",
+    ],
+
+    export_include_dirs: ["include"],
+
+    export_shared_lib_headers: ["libutils"],
+}
diff --git a/libs/hwc2on1adapter/CleanSpec.mk b/libs/hwc2on1adapter/CleanSpec.mk
new file mode 100644
index 0000000..7fc2216
--- /dev/null
+++ b/libs/hwc2on1adapter/CleanSpec.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwc2on1adapter_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwc2on1adapter.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libhwc2on1adapter.so)
diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
new file mode 100644
index 0000000..8c6ef69
--- /dev/null
+++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -0,0 +1,2620 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hwc2on1adapter/HWC2On1Adapter.h"
+
+//#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "HWC2On1Adapter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+
+#include <inttypes.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <sstream>
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+using namespace std::chrono_literals;
+
+static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
+{
+    auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
+    return (version >> 16) & 0xF;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function)
+{
+    static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+    return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+using namespace HWC2;
+
+static constexpr Attribute ColorMode = static_cast<Attribute>(6);
+
+namespace android {
+
+class HWC2On1Adapter::Callbacks : public hwc_procs_t {
+    public:
+        explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+            invalidate = &invalidateHook;
+            vsync = &vsyncHook;
+            hotplug = &hotplugHook;
+        }
+
+        static void invalidateHook(const hwc_procs_t* procs) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Invalidate();
+        }
+
+        static void vsyncHook(const hwc_procs_t* procs, int display,
+                int64_t timestamp) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Vsync(display, timestamp);
+        }
+
+        static void hotplugHook(const hwc_procs_t* procs, int display,
+                int connected) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Hotplug(display, connected);
+        }
+
+    private:
+        HWC2On1Adapter& mAdapter;
+};
+
+static int closeHook(hw_device_t* /*device*/)
+{
+    // Do nothing, since the real work is done in the class destructor, but we
+    // need to provide a valid function pointer for hwc2_close to call
+    return 0;
+}
+
+HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
+  : mDumpString(),
+    mHwc1Device(hwc1Device),
+    mHwc1MinorVersion(getMinorVersion(hwc1Device)),
+    mHwc1SupportsVirtualDisplays(false),
+    mHwc1SupportsBackgroundColor(false),
+    mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
+    mCapabilities(),
+    mLayers(),
+    mHwc1VirtualDisplay(),
+    mStateMutex(),
+    mCallbacks(),
+    mHasPendingInvalidate(false),
+    mPendingVsyncs(),
+    mPendingHotplugs(),
+    mDisplays(),
+    mHwc1DisplayMap()
+{
+    common.close = closeHook;
+    getCapabilities = getCapabilitiesHook;
+    getFunction = getFunctionHook;
+    populateCapabilities();
+    populatePrimary();
+    mHwc1Device->registerProcs(mHwc1Device,
+            static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
+}
+
+HWC2On1Adapter::~HWC2On1Adapter() {
+    hwc_close_1(mHwc1Device);
+}
+
+void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
+        int32_t* outCapabilities) {
+    if (outCapabilities == nullptr) {
+        *outCount = mCapabilities.size();
+        return;
+    }
+
+    auto capabilityIter = mCapabilities.cbegin();
+    for (size_t written = 0; written < *outCount; ++written) {
+        if (capabilityIter == mCapabilities.cend()) {
+            return;
+        }
+        outCapabilities[written] = static_cast<int32_t>(*capabilityIter);
+        ++capabilityIter;
+    }
+}
+
+hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
+        FunctionDescriptor descriptor) {
+    switch (descriptor) {
+        // Device functions
+        case FunctionDescriptor::CreateVirtualDisplay:
+            return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+                    createVirtualDisplayHook);
+        case FunctionDescriptor::DestroyVirtualDisplay:
+            return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+                    destroyVirtualDisplayHook);
+        case FunctionDescriptor::Dump:
+            return asFP<HWC2_PFN_DUMP>(dumpHook);
+        case FunctionDescriptor::GetMaxVirtualDisplayCount:
+            return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+                    getMaxVirtualDisplayCountHook);
+        case FunctionDescriptor::RegisterCallback:
+            return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+        // Display functions
+        case FunctionDescriptor::AcceptDisplayChanges:
+            return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+                    displayHook<decltype(&Display::acceptChanges),
+                    &Display::acceptChanges>);
+        case FunctionDescriptor::CreateLayer:
+            return asFP<HWC2_PFN_CREATE_LAYER>(
+                    displayHook<decltype(&Display::createLayer),
+                    &Display::createLayer, hwc2_layer_t*>);
+        case FunctionDescriptor::DestroyLayer:
+            return asFP<HWC2_PFN_DESTROY_LAYER>(
+                    displayHook<decltype(&Display::destroyLayer),
+                    &Display::destroyLayer, hwc2_layer_t>);
+        case FunctionDescriptor::GetActiveConfig:
+            return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(
+                    displayHook<decltype(&Display::getActiveConfig),
+                    &Display::getActiveConfig, hwc2_config_t*>);
+        case FunctionDescriptor::GetChangedCompositionTypes:
+            return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+                    displayHook<decltype(&Display::getChangedCompositionTypes),
+                    &Display::getChangedCompositionTypes, uint32_t*,
+                    hwc2_layer_t*, int32_t*>);
+        case FunctionDescriptor::GetColorModes:
+            return asFP<HWC2_PFN_GET_COLOR_MODES>(
+                    displayHook<decltype(&Display::getColorModes),
+                    &Display::getColorModes, uint32_t*, int32_t*>);
+        case FunctionDescriptor::GetDisplayAttribute:
+            return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+                    getDisplayAttributeHook);
+        case FunctionDescriptor::GetDisplayConfigs:
+            return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+                    displayHook<decltype(&Display::getConfigs),
+                    &Display::getConfigs, uint32_t*, hwc2_config_t*>);
+        case FunctionDescriptor::GetDisplayName:
+            return asFP<HWC2_PFN_GET_DISPLAY_NAME>(
+                    displayHook<decltype(&Display::getName),
+                    &Display::getName, uint32_t*, char*>);
+        case FunctionDescriptor::GetDisplayRequests:
+            return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+                    displayHook<decltype(&Display::getRequests),
+                    &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*,
+                    int32_t*>);
+        case FunctionDescriptor::GetDisplayType:
+            return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(
+                    displayHook<decltype(&Display::getType),
+                    &Display::getType, int32_t*>);
+        case FunctionDescriptor::GetDozeSupport:
+            return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
+                    displayHook<decltype(&Display::getDozeSupport),
+                    &Display::getDozeSupport, int32_t*>);
+        case FunctionDescriptor::GetHdrCapabilities:
+            return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(
+                    displayHook<decltype(&Display::getHdrCapabilities),
+                    &Display::getHdrCapabilities, uint32_t*, int32_t*, float*,
+                    float*, float*>);
+        case FunctionDescriptor::GetReleaseFences:
+            return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
+                    displayHook<decltype(&Display::getReleaseFences),
+                    &Display::getReleaseFences, uint32_t*, hwc2_layer_t*,
+                    int32_t*>);
+        case FunctionDescriptor::PresentDisplay:
+            return asFP<HWC2_PFN_PRESENT_DISPLAY>(
+                    displayHook<decltype(&Display::present),
+                    &Display::present, int32_t*>);
+        case FunctionDescriptor::SetActiveConfig:
+            return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(
+                    displayHook<decltype(&Display::setActiveConfig),
+                    &Display::setActiveConfig, hwc2_config_t>);
+        case FunctionDescriptor::SetClientTarget:
+            return asFP<HWC2_PFN_SET_CLIENT_TARGET>(
+                    displayHook<decltype(&Display::setClientTarget),
+                    &Display::setClientTarget, buffer_handle_t, int32_t,
+                    int32_t, hwc_region_t>);
+        case FunctionDescriptor::SetColorMode:
+            return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
+        case FunctionDescriptor::SetColorTransform:
+            return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
+        case FunctionDescriptor::SetOutputBuffer:
+            return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
+                    displayHook<decltype(&Display::setOutputBuffer),
+                    &Display::setOutputBuffer, buffer_handle_t, int32_t>);
+        case FunctionDescriptor::SetPowerMode:
+            return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+        case FunctionDescriptor::SetVsyncEnabled:
+            return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+        case FunctionDescriptor::ValidateDisplay:
+            return asFP<HWC2_PFN_VALIDATE_DISPLAY>(
+                    displayHook<decltype(&Display::validate),
+                    &Display::validate, uint32_t*, uint32_t*>);
+        case FunctionDescriptor::GetClientTargetSupport:
+            return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
+                    displayHook<decltype(&Display::getClientTargetSupport),
+                    &Display::getClientTargetSupport, uint32_t, uint32_t,
+                                                      int32_t, int32_t>);
+
+        // Layer functions
+        case FunctionDescriptor::SetCursorPosition:
+            return asFP<HWC2_PFN_SET_CURSOR_POSITION>(
+                    layerHook<decltype(&Layer::setCursorPosition),
+                    &Layer::setCursorPosition, int32_t, int32_t>);
+        case FunctionDescriptor::SetLayerBuffer:
+            return asFP<HWC2_PFN_SET_LAYER_BUFFER>(
+                    layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer,
+                    buffer_handle_t, int32_t>);
+        case FunctionDescriptor::SetLayerSurfaceDamage:
+            return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+                    layerHook<decltype(&Layer::setSurfaceDamage),
+                    &Layer::setSurfaceDamage, hwc_region_t>);
+
+        // Layer state functions
+        case FunctionDescriptor::SetLayerBlendMode:
+            return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+                    setLayerBlendModeHook);
+        case FunctionDescriptor::SetLayerColor:
+            return asFP<HWC2_PFN_SET_LAYER_COLOR>(
+                    layerHook<decltype(&Layer::setColor), &Layer::setColor,
+                    hwc_color_t>);
+        case FunctionDescriptor::SetLayerCompositionType:
+            return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+                    setLayerCompositionTypeHook);
+        case FunctionDescriptor::SetLayerDataspace:
+            return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook);
+        case FunctionDescriptor::SetLayerDisplayFrame:
+            return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+                    layerHook<decltype(&Layer::setDisplayFrame),
+                    &Layer::setDisplayFrame, hwc_rect_t>);
+        case FunctionDescriptor::SetLayerPlaneAlpha:
+            return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+                    layerHook<decltype(&Layer::setPlaneAlpha),
+                    &Layer::setPlaneAlpha, float>);
+        case FunctionDescriptor::SetLayerSidebandStream:
+            return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
+                    layerHook<decltype(&Layer::setSidebandStream),
+                    &Layer::setSidebandStream, const native_handle_t*>);
+        case FunctionDescriptor::SetLayerSourceCrop:
+            return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+                    layerHook<decltype(&Layer::setSourceCrop),
+                    &Layer::setSourceCrop, hwc_frect_t>);
+        case FunctionDescriptor::SetLayerTransform:
+            return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook);
+        case FunctionDescriptor::SetLayerVisibleRegion:
+            return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+                    layerHook<decltype(&Layer::setVisibleRegion),
+                    &Layer::setVisibleRegion, hwc_region_t>);
+        case FunctionDescriptor::SetLayerZOrder:
+            return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook);
+
+        default:
+            ALOGE("doGetFunction: Unknown function descriptor: %d (%s)",
+                    static_cast<int32_t>(descriptor),
+                    to_string(descriptor).c_str());
+            return nullptr;
+    }
+}
+
+// Device functions
+
+Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
+        uint32_t height, hwc2_display_t* outDisplay) {
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    if (mHwc1VirtualDisplay) {
+        // We have already allocated our only HWC1 virtual display
+        ALOGE("createVirtualDisplay: HWC1 virtual display already allocated");
+        return Error::NoResources;
+    }
+
+    mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this,
+            HWC2::DisplayType::Virtual);
+    mHwc1VirtualDisplay->populateConfigs(width, height);
+    const auto displayId = mHwc1VirtualDisplay->getId();
+    mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId;
+    mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL);
+    mDisplays.emplace(displayId, mHwc1VirtualDisplay);
+    *outDisplay = displayId;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) {
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
+        return Error::BadDisplay;
+    }
+
+    mHwc1VirtualDisplay.reset();
+    mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL);
+    mDisplays.erase(displayId);
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) {
+    if (outBuffer != nullptr) {
+        auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
+        *outSize = static_cast<uint32_t>(copiedBytes);
+        return;
+    }
+
+    std::stringstream output;
+
+    output << "-- HWC2On1Adapter --\n";
+
+    output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) <<
+            " device\n";
+
+    // Attempt to acquire the lock for 1 second, but proceed without the lock
+    // after that, so we can still get some information if we're deadlocked
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex,
+            std::defer_lock);
+    lock.try_lock_for(1s);
+
+    if (mCapabilities.empty()) {
+        output << "Capabilities: None\n";
+    } else {
+        output << "Capabilities:\n";
+        for (auto capability : mCapabilities) {
+            output << "  " << to_string(capability) << '\n';
+        }
+    }
+
+    output << "Displays:\n";
+    for (const auto& element : mDisplays) {
+        const auto& display = element.second;
+        output << display->dump();
+    }
+    output << '\n';
+
+    // Release the lock before calling into HWC1, and since we no longer require
+    // mutual exclusion to access mCapabilities or mDisplays
+    lock.unlock();
+
+    if (mHwc1Device->dump) {
+        output << "HWC1 dump:\n";
+        std::vector<char> hwc1Dump(4096);
+        // Call with size - 1 to preserve a null character at the end
+        mHwc1Device->dump(mHwc1Device, hwc1Dump.data(),
+                static_cast<int>(hwc1Dump.size() - 1));
+        output << hwc1Dump.data();
+    }
+
+    mDumpString = output.str();
+    *outSize = static_cast<uint32_t>(mDumpString.size());
+}
+
+uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() {
+    return mHwc1SupportsVirtualDisplays ? 1 : 0;
+}
+
+static bool isValid(Callback descriptor) {
+    switch (descriptor) {
+        case Callback::Hotplug: // Fall-through
+        case Callback::Refresh: // Fall-through
+        case Callback::Vsync: return true;
+        default: return false;
+    }
+}
+
+Error HWC2On1Adapter::registerCallback(Callback descriptor,
+        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
+    if (!isValid(descriptor)) {
+        return Error::BadParameter;
+    }
+
+    ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(),
+            callbackData, pointer);
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    mCallbacks[descriptor] = {callbackData, pointer};
+
+    bool hasPendingInvalidate = false;
+    std::vector<hwc2_display_t> displayIds;
+    std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
+    std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;
+
+    if (descriptor == Callback::Refresh) {
+        hasPendingInvalidate = mHasPendingInvalidate;
+        if (hasPendingInvalidate) {
+            for (auto& displayPair : mDisplays) {
+                displayIds.emplace_back(displayPair.first);
+            }
+        }
+        mHasPendingInvalidate = false;
+    } else if (descriptor == Callback::Vsync) {
+        for (auto pending : mPendingVsyncs) {
+            auto hwc1DisplayId = pending.first;
+            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+                ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
+                        hwc1DisplayId);
+                continue;
+            }
+            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+            auto timestamp = pending.second;
+            pendingVsyncs.emplace_back(displayId, timestamp);
+        }
+        mPendingVsyncs.clear();
+    } else if (descriptor == Callback::Hotplug) {
+        // Hotplug the primary display
+        pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
+                static_cast<int32_t>(Connection::Connected));
+
+        for (auto pending : mPendingHotplugs) {
+            auto hwc1DisplayId = pending.first;
+            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+                ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
+                        hwc1DisplayId);
+                continue;
+            }
+            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+            auto connected = pending.second;
+            pendingHotplugs.emplace_back(displayId, connected);
+        }
+    }
+
+    // Call pending callbacks without the state lock held
+    lock.unlock();
+
+    if (hasPendingInvalidate) {
+        auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
+        for (auto displayId : displayIds) {
+            refresh(callbackData, displayId);
+        }
+    }
+    if (!pendingVsyncs.empty()) {
+        auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
+        for (auto& pendingVsync : pendingVsyncs) {
+            vsync(callbackData, pendingVsync.first, pendingVsync.second);
+        }
+    }
+    if (!pendingHotplugs.empty()) {
+        auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
+        for (auto& pendingHotplug : pendingHotplugs) {
+            hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
+        }
+    }
+    return Error::None;
+}
+
+// Display functions
+
+std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1);
+
+HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
+  : mId(sNextId++),
+    mDevice(device),
+    mStateMutex(),
+    mHwc1RequestedContents(nullptr),
+    mRetireFence(),
+    mChanges(),
+    mHwc1Id(-1),
+    mConfigs(),
+    mActiveConfig(nullptr),
+    mActiveColorMode(static_cast<android_color_mode_t>(-1)),
+    mName(),
+    mType(type),
+    mPowerMode(PowerMode::Off),
+    mVsyncEnabled(Vsync::Invalid),
+    mClientTarget(),
+    mOutputBuffer(),
+    mHasColorTransform(false),
+    mLayers(),
+    mHwc1LayerMap(),
+    mNumAvailableRects(0),
+    mNextAvailableRect(nullptr),
+    mGeometryChanged(false)
+    {}
+
+Error HWC2On1Adapter::Display::acceptChanges() {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId);
+        return Error::NotValidated;
+    }
+
+    ALOGV("[%" PRIu64 "] acceptChanges", mId);
+
+    for (auto& change : mChanges->getTypeChanges()) {
+        auto layerId = change.first;
+        auto type = change.second;
+        if (mDevice.mLayers.count(layerId) == 0) {
+            // This should never happen but somehow does.
+            ALOGW("Cannot accept change for unknown layer (%" PRIu64 ")",
+                  layerId);
+            continue;
+        }
+        auto layer = mDevice.mLayers[layerId];
+        layer->setCompositionType(type);
+    }
+
+    mChanges->clearTypeChanges();
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
+    mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
+    *outLayerId = layer->getId();
+    ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+    markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    const auto mapLayer = mDevice.mLayers.find(layerId);
+    if (mapLayer == mDevice.mLayers.end()) {
+        ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer",
+                mId, layerId);
+        return Error::BadLayer;
+    }
+    const auto layer = mapLayer->second;
+    mDevice.mLayers.erase(mapLayer);
+    const auto zRange = mLayers.equal_range(layer);
+    for (auto current = zRange.first; current != zRange.second; ++current) {
+        if (**current == *layer) {
+            current = mLayers.erase(current);
+            break;
+        }
+    }
+    ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+    markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mActiveConfig) {
+        ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId,
+                to_string(Error::BadConfig).c_str());
+        return Error::BadConfig;
+    }
+    auto configId = mActiveConfig->getId();
+    ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId);
+    *outConfig = configId;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
+        Attribute attribute, int32_t* outValue) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+        ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId,
+                configId);
+        return Error::BadConfig;
+    }
+    *outValue = mConfigs[configId]->getAttribute(attribute);
+    ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId,
+            to_string(attribute).c_str(), *outValue);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getChangedCompositionTypes(
+        uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated",
+                mId);
+        return Error::NotValidated;
+    }
+
+    if ((outLayers == nullptr) || (outTypes == nullptr)) {
+        *outNumElements = mChanges->getTypeChanges().size();
+        return Error::None;
+    }
+
+    uint32_t numWritten = 0;
+    for (const auto& element : mChanges->getTypeChanges()) {
+        if (numWritten == *outNumElements) {
+            break;
+        }
+        auto layerId = element.first;
+        auto intType = static_cast<int32_t>(element.second);
+        ALOGV("Adding %" PRIu64 " %s", layerId,
+                to_string(element.second).c_str());
+        outLayers[numWritten] = layerId;
+        outTypes[numWritten] = intType;
+        ++numWritten;
+    }
+    *outNumElements = numWritten;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
+        int32_t* outModes) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outModes) {
+        *outNumModes = mColorModes.size();
+        return Error::None;
+    }
+    uint32_t numModes = std::min(*outNumModes,
+            static_cast<uint32_t>(mColorModes.size()));
+    std::copy_n(mColorModes.cbegin(), numModes, outModes);
+    *outNumModes = numModes;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
+        hwc2_config_t* outConfigs) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outConfigs) {
+        *outNumConfigs = mConfigs.size();
+        return Error::None;
+    }
+    uint32_t numWritten = 0;
+    for (const auto& config : mConfigs) {
+        if (numWritten == *outNumConfigs) {
+            break;
+        }
+        outConfigs[numWritten] = config->getId();
+        ++numWritten;
+    }
+    *outNumConfigs = numWritten;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
+        *outSupport = 0;
+    } else {
+        *outSupport = 1;
+    }
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
+        int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+        float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+    // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
+    *outNumTypes = 0;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outName) {
+        *outSize = mName.size();
+        return Error::None;
+    }
+    auto numCopied = mName.copy(outName, *outSize);
+    *outSize = numCopied;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
+        hwc2_layer_t* outLayers, int32_t* outFences) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    uint32_t numWritten = 0;
+    bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr);
+    for (const auto& layer : mLayers) {
+        if (outputsNonNull && (numWritten == *outNumElements)) {
+            break;
+        }
+
+        auto releaseFence = layer->getReleaseFence();
+        if (releaseFence != MiniFence::NO_FENCE) {
+            if (outputsNonNull) {
+                outLayers[numWritten] = layer->getId();
+                outFences[numWritten] = releaseFence->dup();
+            }
+            ++numWritten;
+        }
+    }
+    *outNumElements = numWritten;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
+        uint32_t* outNumElements, hwc2_layer_t* outLayers,
+        int32_t* outLayerRequests) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        return Error::NotValidated;
+    }
+
+    if (outLayers == nullptr || outLayerRequests == nullptr) {
+        *outNumElements = mChanges->getNumLayerRequests();
+        return Error::None;
+    }
+
+    // Display requests (HWC2::DisplayRequest) are not supported by hwc1:
+    // A hwc1 has always zero requests for the client.
+    *outDisplayRequests = 0;
+
+    uint32_t numWritten = 0;
+    for (const auto& request : mChanges->getLayerRequests()) {
+        if (numWritten == *outNumElements) {
+            break;
+        }
+        outLayers[numWritten] = request.first;
+        outLayerRequests[numWritten] = static_cast<int32_t>(request.second);
+        ++numWritten;
+    }
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getType(int32_t* outType) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    *outType = static_cast<int32_t>(mType);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (mChanges) {
+        Error error = mDevice.setAllDisplays();
+        if (error != Error::None) {
+            ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId,
+                    to_string(error).c_str());
+            return error;
+        }
+    }
+
+    *outRetireFence = mRetireFence.get()->dup();
+    ALOGV("[%" PRIu64 "] present returning retire fence %d", mId,
+            *outRetireFence);
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    auto config = getConfig(configId);
+    if (!config) {
+        return Error::BadConfig;
+    }
+    if (config == mActiveConfig) {
+        return Error::None;
+    }
+
+    if (mDevice.mHwc1MinorVersion >= 4) {
+        uint32_t hwc1Id = 0;
+        auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id);
+        if (error != Error::None) {
+            return error;
+        }
+
+        int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+                mHwc1Id, static_cast<int>(hwc1Id));
+        if (intError != 0) {
+            ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)",
+                intError);
+            return Error::BadConfig;
+        }
+        mActiveConfig = config;
+    }
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
+        int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
+    mClientTarget.setBuffer(target);
+    mClientTarget.setFence(acquireFence);
+    // dataspace and damage can't be used by HWC1, so ignore them
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) {
+    std::unique_lock<std::recursive_mutex> lock (mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
+
+    if (mode == mActiveColorMode) {
+        return Error::None;
+    }
+    if (mColorModes.count(mode) == 0) {
+        ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode);
+        return Error::Unsupported;
+    }
+
+    uint32_t hwc1Config = 0;
+    auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config);
+    if (error != Error::None) {
+        return error;
+    }
+
+    ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config);
+    int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+            mHwc1Id, hwc1Config);
+    if (intError != 0) {
+        ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError);
+        return Error::Unsupported;
+    }
+
+    mActiveColorMode = mode;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
+            static_cast<int32_t>(hint));
+    mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
+        int32_t releaseFence) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
+    mOutputBuffer.setBuffer(buffer);
+    mOutputBuffer.setFence(releaseFence);
+    return Error::None;
+}
+
+static bool isValid(PowerMode mode) {
+    switch (mode) {
+        case PowerMode::Off: // Fall-through
+        case PowerMode::DozeSuspend: // Fall-through
+        case PowerMode::Doze: // Fall-through
+        case PowerMode::On: return true;
+    }
+}
+
+static int getHwc1PowerMode(PowerMode mode) {
+    switch (mode) {
+        case PowerMode::Off: return HWC_POWER_MODE_OFF;
+        case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
+        case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
+        case PowerMode::On: return HWC_POWER_MODE_NORMAL;
+    }
+}
+
+Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) {
+    if (!isValid(mode)) {
+        return Error::BadParameter;
+    }
+    if (mode == mPowerMode) {
+        return Error::None;
+    }
+
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    int error = 0;
+    if (mDevice.mHwc1MinorVersion < 4) {
+        error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id,
+                mode == PowerMode::Off);
+    } else {
+        error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device,
+                mHwc1Id, getHwc1PowerMode(mode));
+    }
+    ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)",
+            error);
+
+    ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str());
+    mPowerMode = mode;
+    return Error::None;
+}
+
+static bool isValid(Vsync enable) {
+    switch (enable) {
+        case Vsync::Enable: // Fall-through
+        case Vsync::Disable: return true;
+        case Vsync::Invalid: return false;
+    }
+}
+
+Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) {
+    if (!isValid(enable)) {
+        return Error::BadParameter;
+    }
+    if (enable == mVsyncEnabled) {
+        return Error::None;
+    }
+
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device,
+            mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable);
+    ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)",
+            error);
+
+    mVsyncEnabled = enable;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
+        uint32_t* outNumRequests) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        if (!mDevice.prepareAllDisplays()) {
+            return Error::BadDisplay;
+        }
+    } else {
+        ALOGE("Validate was called more than once!");
+    }
+
+    *outNumTypes = mChanges->getNumTypes();
+    *outNumRequests = mChanges->getNumLayerRequests();
+    ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes,
+            *outNumRequests);
+    for (auto request : mChanges->getTypeChanges()) {
+        ALOGV("Layer %" PRIu64 " --> %s", request.first,
+                to_string(request.second).c_str());
+    }
+    return *outNumTypes > 0 ? Error::HasChanges : Error::None;
+}
+
+Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    const auto mapLayer = mDevice.mLayers.find(layerId);
+    if (mapLayer == mDevice.mLayers.end()) {
+        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId);
+        return Error::BadLayer;
+    }
+
+    const auto layer = mapLayer->second;
+    const auto zRange = mLayers.equal_range(layer);
+    bool layerOnDisplay = false;
+    for (auto current = zRange.first; current != zRange.second; ++current) {
+        if (**current == *layer) {
+            if ((*current)->getZ() == z) {
+                // Don't change anything if the Z hasn't changed
+                return Error::None;
+            }
+            current = mLayers.erase(current);
+            layerOnDisplay = true;
+            break;
+        }
+    }
+
+    if (!layerOnDisplay) {
+        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display",
+                mId);
+        return Error::BadLayer;
+    }
+
+    layer->setZ(z);
+    mLayers.emplace(std::move(layer));
+    markGeometryChanged();
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getClientTargetSupport(uint32_t width, uint32_t height,
+                                      int32_t format, int32_t dataspace){
+    if (mActiveConfig == nullptr) {
+        return Error::Unsupported;
+    }
+
+    if (width == mActiveConfig->getAttribute(Attribute::Width) &&
+            height == mActiveConfig->getAttribute(Attribute::Height) &&
+            format == HAL_PIXEL_FORMAT_RGBA_8888 &&
+            dataspace == HAL_DATASPACE_UNKNOWN) {
+        return Error::None;
+    }
+
+    return Error::Unsupported;
+}
+
+static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = {
+    HWC_DISPLAY_VSYNC_PERIOD,
+    HWC_DISPLAY_WIDTH,
+    HWC_DISPLAY_HEIGHT,
+    HWC_DISPLAY_DPI_X,
+    HWC_DISPLAY_DPI_Y,
+    HWC_DISPLAY_COLOR_TRANSFORM,
+    HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = {
+    HWC_DISPLAY_VSYNC_PERIOD,
+    HWC_DISPLAY_WIDTH,
+    HWC_DISPLAY_HEIGHT,
+    HWC_DISPLAY_DPI_X,
+    HWC_DISPLAY_DPI_Y,
+    HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR =
+        sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t);
+static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR),
+        "Attribute tables have unexpected sizes");
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = {
+    6, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+    1, // HWC_DISPLAY_WIDTH = 2,
+    2, // HWC_DISPLAY_HEIGHT = 3,
+    3, // HWC_DISPLAY_DPI_X = 4,
+    4, // HWC_DISPLAY_DPI_Y = 5,
+    5, // HWC_DISPLAY_COLOR_TRANSFORM = 6,
+};
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = {
+    5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+    1, // HWC_DISPLAY_WIDTH = 2,
+    2, // HWC_DISPLAY_HEIGHT = 3,
+    3, // HWC_DISPLAY_DPI_X = 4,
+    4, // HWC_DISPLAY_DPI_Y = 5,
+};
+
+template <uint32_t attribute>
+static constexpr bool attributesMatch()
+{
+    bool match = (attribute ==
+            ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]);
+    if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) {
+        return match;
+    }
+
+    return match && (attribute ==
+            ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]);
+}
+static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
+        "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
+        "Tables out of sync");
+
+void HWC2On1Adapter::Display::populateConfigs() {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] populateConfigs", mId);
+
+    if (mHwc1Id == -1) {
+        ALOGE("populateConfigs: HWC1 ID not set");
+        return;
+    }
+
+    const size_t MAX_NUM_CONFIGS = 128;
+    uint32_t configs[MAX_NUM_CONFIGS] = {};
+    size_t numConfigs = MAX_NUM_CONFIGS;
+    mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id,
+            configs, &numConfigs);
+
+    for (size_t c = 0; c < numConfigs; ++c) {
+        uint32_t hwc1ConfigId = configs[c];
+        auto newConfig = std::make_shared<Config>(*this);
+
+        int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {};
+        bool hasColor = true;
+        auto result = mDevice.mHwc1Device->getDisplayAttributes(
+                mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId,
+                ATTRIBUTES_WITH_COLOR, values);
+        if (result != 0) {
+            mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device,
+                    mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values);
+            hasColor = false;
+        }
+
+        auto attributeMap = hasColor ?
+                ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR;
+
+        newConfig->setAttribute(Attribute::VsyncPeriod,
+                values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]);
+        newConfig->setAttribute(Attribute::Width,
+                values[attributeMap[HWC_DISPLAY_WIDTH]]);
+        newConfig->setAttribute(Attribute::Height,
+                values[attributeMap[HWC_DISPLAY_HEIGHT]]);
+        newConfig->setAttribute(Attribute::DpiX,
+                values[attributeMap[HWC_DISPLAY_DPI_X]]);
+        newConfig->setAttribute(Attribute::DpiY,
+                values[attributeMap[HWC_DISPLAY_DPI_Y]]);
+        if (hasColor) {
+            // In HWC1, color modes are referred to as color transforms. To avoid confusion with
+            // the HWC2 concept of color transforms, we internally refer to them as color modes for
+            // both HWC1 and 2.
+            newConfig->setAttribute(ColorMode,
+                    values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
+        }
+
+        // We can only do this after attempting to read the color mode
+        newConfig->setHwc1Id(hwc1ConfigId);
+
+        for (auto& existingConfig : mConfigs) {
+            if (existingConfig->merge(*newConfig)) {
+                ALOGV("Merged config %d with existing config %u: %s",
+                        hwc1ConfigId, existingConfig->getId(),
+                        existingConfig->toString().c_str());
+                newConfig.reset();
+                break;
+            }
+        }
+
+        // If it wasn't merged with any existing config, add it to the end
+        if (newConfig) {
+            newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
+            ALOGV("Found new config %u: %s", newConfig->getId(),
+                    newConfig->toString().c_str());
+            mConfigs.emplace_back(std::move(newConfig));
+        }
+    }
+
+    initializeActiveConfig();
+    populateColorModes();
+}
+
+void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    mConfigs.emplace_back(std::make_shared<Config>(*this));
+    auto& config = mConfigs[0];
+
+    config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
+    config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
+    config->setHwc1Id(0);
+    config->setId(0);
+    mActiveConfig = config;
+}
+
+bool HWC2On1Adapter::Display::prepare() {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    // Only prepare display contents for displays HWC1 knows about
+    if (mHwc1Id == -1) {
+        return true;
+    }
+
+    // It doesn't make sense to prepare a display for which there is no active
+    // config, so return early
+    if (!mActiveConfig) {
+        ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId);
+        return false;
+    }
+
+    allocateRequestedContents();
+    assignHwc1LayerIds();
+
+    mHwc1RequestedContents->retireFenceFd = -1;
+    mHwc1RequestedContents->flags = 0;
+    if (mGeometryChanged) {
+        mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
+    }
+    mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
+    mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
+
+    // +1 is for framebuffer target layer.
+    mHwc1RequestedContents->numHwLayers = mLayers.size() + 1;
+    for (auto& layer : mLayers) {
+        auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
+        hwc1Layer.releaseFenceFd = -1;
+        hwc1Layer.acquireFenceFd = -1;
+        ALOGV("Applying states for layer %" PRIu64 " ", layer->getId());
+        layer->applyState(hwc1Layer);
+    }
+
+    prepareFramebufferTarget();
+
+    resetGeometryMarker();
+
+    return true;
+}
+
+void HWC2On1Adapter::Display::generateChanges() {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    mChanges.reset(new Changes);
+
+    size_t numLayers = mHwc1RequestedContents->numHwLayers;
+    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+        const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id];
+        if (mHwc1LayerMap.count(hwc1Id) == 0) {
+            ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
+                    "generateChanges: HWC1 layer %zd doesn't have a"
+                    " matching HWC2 layer, and isn't the framebuffer target",
+                    hwc1Id);
+            continue;
+        }
+
+        Layer& layer = *mHwc1LayerMap[hwc1Id];
+        updateTypeChanges(receivedLayer, layer);
+        updateLayerRequests(receivedLayer, layer);
+    }
+}
+
+bool HWC2On1Adapter::Display::hasChanges() const {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    return mChanges != nullptr;
+}
+
+Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges || (mChanges->getNumTypes() > 0)) {
+        ALOGE("[%" PRIu64 "] set failed: not validated", mId);
+        return Error::NotValidated;
+    }
+
+    // Set up the client/framebuffer target
+    auto numLayers = hwcContents.numHwLayers;
+
+    // Close acquire fences on FRAMEBUFFER layers, since they will not be used
+    // by HWC
+    for (size_t l = 0; l < numLayers - 1; ++l) {
+        auto& layer = hwcContents.hwLayers[l];
+        if (layer.compositionType == HWC_FRAMEBUFFER) {
+            ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l);
+            close(layer.acquireFenceFd);
+            layer.acquireFenceFd = -1;
+        }
+    }
+
+    auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1];
+    if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+        clientTargetLayer.handle = mClientTarget.getBuffer();
+        clientTargetLayer.acquireFenceFd = mClientTarget.getFence();
+    } else {
+        ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET",
+                mId);
+    }
+
+    mChanges.reset();
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::Display::addRetireFence(int fenceFd) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    mRetireFence.add(fenceFd);
+}
+
+void HWC2On1Adapter::Display::addReleaseFences(
+        const hwc_display_contents_1_t& hwcContents) {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    size_t numLayers = hwcContents.numHwLayers;
+    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+        const auto& receivedLayer = hwcContents.hwLayers[hwc1Id];
+        if (mHwc1LayerMap.count(hwc1Id) == 0) {
+            if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) {
+                ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a"
+                        " matching HWC2 layer, and isn't the framebuffer"
+                        " target", hwc1Id);
+            }
+            // Close the framebuffer target release fence since we will use the
+            // display retire fence instead
+            if (receivedLayer.releaseFenceFd != -1) {
+                close(receivedLayer.releaseFenceFd);
+            }
+            continue;
+        }
+
+        Layer& layer = *mHwc1LayerMap[hwc1Id];
+        ALOGV("Adding release fence %d to layer %" PRIu64,
+                receivedLayer.releaseFenceFd, layer.getId());
+        layer.addReleaseFence(receivedLayer.releaseFenceFd);
+    }
+}
+
+bool HWC2On1Adapter::Display::hasColorTransform() const {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    return mHasColorTransform;
+}
+
+static std::string hwc1CompositionString(int32_t type) {
+    switch (type) {
+        case HWC_FRAMEBUFFER: return "Framebuffer";
+        case HWC_OVERLAY: return "Overlay";
+        case HWC_BACKGROUND: return "Background";
+        case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget";
+        case HWC_SIDEBAND: return "Sideband";
+        case HWC_CURSOR_OVERLAY: return "CursorOverlay";
+        default:
+            return std::string("Unknown (") + std::to_string(type) + ")";
+    }
+}
+
+static std::string hwc1TransformString(int32_t transform) {
+    switch (transform) {
+        case 0: return "None";
+        case HWC_TRANSFORM_FLIP_H: return "FlipH";
+        case HWC_TRANSFORM_FLIP_V: return "FlipV";
+        case HWC_TRANSFORM_ROT_90: return "Rotate90";
+        case HWC_TRANSFORM_ROT_180: return "Rotate180";
+        case HWC_TRANSFORM_ROT_270: return "Rotate270";
+        case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90";
+        case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90";
+        default:
+            return std::string("Unknown (") + std::to_string(transform) + ")";
+    }
+}
+
+static std::string hwc1BlendModeString(int32_t mode) {
+    switch (mode) {
+        case HWC_BLENDING_NONE: return "None";
+        case HWC_BLENDING_PREMULT: return "Premultiplied";
+        case HWC_BLENDING_COVERAGE: return "Coverage";
+        default:
+            return std::string("Unknown (") + std::to_string(mode) + ")";
+    }
+}
+
+static std::string rectString(hwc_rect_t rect) {
+    std::stringstream output;
+    output << "[" << rect.left << ", " << rect.top << ", ";
+    output << rect.right << ", " << rect.bottom << "]";
+    return output.str();
+}
+
+static std::string approximateFloatString(float f) {
+    if (static_cast<int32_t>(f) == f) {
+        return std::to_string(static_cast<int32_t>(f));
+    }
+    int32_t truncated = static_cast<int32_t>(f * 10);
+    bool approximate = (static_cast<float>(truncated) != f * 10);
+    const size_t BUFFER_SIZE = 32;
+    char buffer[BUFFER_SIZE] = {};
+    auto bytesWritten = snprintf(buffer, BUFFER_SIZE,
+            "%s%.1f", approximate ? "~" : "", f);
+    return std::string(buffer, bytesWritten);
+}
+
+static std::string frectString(hwc_frect_t frect) {
+    std::stringstream output;
+    output << "[" << approximateFloatString(frect.left) << ", ";
+    output << approximateFloatString(frect.top) << ", ";
+    output << approximateFloatString(frect.right) << ", ";
+    output << approximateFloatString(frect.bottom) << "]";
+    return output.str();
+}
+
+static std::string colorString(hwc_color_t color) {
+    std::stringstream output;
+    output << "RGBA [";
+    output << static_cast<int32_t>(color.r) << ", ";
+    output << static_cast<int32_t>(color.g) << ", ";
+    output << static_cast<int32_t>(color.b) << ", ";
+    output << static_cast<int32_t>(color.a) << "]";
+    return output.str();
+}
+
+static std::string alphaString(float f) {
+    const size_t BUFFER_SIZE = 8;
+    char buffer[BUFFER_SIZE] = {};
+    auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
+    return std::string(buffer, bytesWritten);
+}
+
+static std::string to_string(const hwc_layer_1_t& hwcLayer,
+        int32_t hwc1MinorVersion) {
+    const char* fill = "          ";
+
+    std::stringstream output;
+
+    output << "  Composition: " <<
+            hwc1CompositionString(hwcLayer.compositionType);
+
+    if (hwcLayer.compositionType == HWC_BACKGROUND) {
+        output << "  Color: " << colorString(hwcLayer.backgroundColor) << '\n';
+    } else if (hwcLayer.compositionType == HWC_SIDEBAND) {
+        output << "  Stream: " << hwcLayer.sidebandStream << '\n';
+    } else {
+        output << "  Buffer: " << hwcLayer.handle << "/" <<
+                hwcLayer.acquireFenceFd << '\n';
+    }
+
+    output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) <<
+            '\n';
+
+    output << fill << "Source crop: ";
+    if (hwc1MinorVersion >= 3) {
+        output << frectString(hwcLayer.sourceCropf) << '\n';
+    } else {
+        output << rectString(hwcLayer.sourceCropi) << '\n';
+    }
+
+    output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform);
+    output << "  Blend mode: " << hwc1BlendModeString(hwcLayer.blending);
+    if (hwcLayer.planeAlpha != 0xFF) {
+        output << "  Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f);
+    }
+    output << '\n';
+
+    if (hwcLayer.hints != 0) {
+        output << fill << "Hints:";
+        if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) {
+            output << " TripleBuffer";
+        }
+        if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) {
+            output << " ClearFB";
+        }
+        output << '\n';
+    }
+
+    if (hwcLayer.flags != 0) {
+        output << fill << "Flags:";
+        if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) {
+            output << " SkipLayer";
+        }
+        if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) {
+            output << " IsCursorLayer";
+        }
+        output << '\n';
+    }
+
+    return output.str();
+}
+
+static std::string to_string(const hwc_display_contents_1_t& hwcContents,
+        int32_t hwc1MinorVersion) {
+    const char* fill = "      ";
+
+    std::stringstream output;
+    output << fill << "Geometry changed: " <<
+            ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n");
+
+    output << fill << hwcContents.numHwLayers << " Layer" <<
+            ((hwcContents.numHwLayers == 1) ? "\n" : "s\n");
+    for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) {
+        output << fill << "  Layer " << layer;
+        output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion);
+    }
+
+    if (hwcContents.outbuf != nullptr) {
+        output << fill << "Output buffer: " << hwcContents.outbuf << "/" <<
+                hwcContents.outbufAcquireFenceFd << '\n';
+    }
+
+    return output.str();
+}
+
+std::string HWC2On1Adapter::Display::dump() const {
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    std::stringstream output;
+
+    output << "  Display " << mId << ": ";
+    output << to_string(mType) << "  ";
+    output << "HWC1 ID: " << mHwc1Id << "  ";
+    output << "Power mode: " << to_string(mPowerMode) << "  ";
+    output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
+
+    output << "    Color modes [active]:";
+    for (const auto& mode : mColorModes) {
+        if (mode == mActiveColorMode) {
+            output << " [" << mode << ']';
+        } else {
+            output << " " << mode;
+        }
+    }
+    output << '\n';
+
+    output << "    " << mConfigs.size() << " Config" <<
+            (mConfigs.size() == 1 ? "" : "s") << " (* active)\n";
+    for (const auto& config : mConfigs) {
+        output << (config == mActiveConfig ? "    * " : "      ");
+        output << config->toString(true) << '\n';
+    }
+
+    output << "    " << mLayers.size() << " Layer" <<
+            (mLayers.size() == 1 ? "" : "s") << '\n';
+    for (const auto& layer : mLayers) {
+        output << layer->dump();
+    }
+
+    output << "    Client target: " << mClientTarget.getBuffer() << '\n';
+
+    if (mOutputBuffer.getBuffer() != nullptr) {
+        output << "    Output buffer: " << mOutputBuffer.getBuffer() << '\n';
+    }
+
+    if (mHwc1RequestedContents) {
+        output << "    Last requested HWC1 state\n";
+        output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
+    }
+
+    return output.str();
+}
+
+hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) {
+    if (numRects == 0) {
+        return nullptr;
+    }
+
+    if (numRects > mNumAvailableRects) {
+        // This should NEVER happen since we calculated how many rects the
+        // display would need.
+        ALOGE("Rect allocation failure! SF is likely to crash soon!");
+        return nullptr;
+
+    }
+    hwc_rect_t* rects = mNextAvailableRect;
+    mNextAvailableRect += numRects;
+    mNumAvailableRects -= numRects;
+    return rects;
+}
+
+hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() {
+    return mHwc1RequestedContents.get();
+}
+
+void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
+        int32_t value) {
+    mAttributes[attribute] = value;
+}
+
+int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const {
+    if (mAttributes.count(attribute) == 0) {
+        return -1;
+    }
+    return mAttributes.at(attribute);
+}
+
+void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) {
+    android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
+    mHwc1Ids.emplace(colorMode, id);
+}
+
+bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const {
+    for (const auto& idPair : mHwc1Ids) {
+        if (id == idPair.second) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
+        uint32_t id, android_color_mode_t* outMode) const {
+    for (const auto& idPair : mHwc1Ids) {
+        if (id == idPair.second) {
+            *outMode = idPair.first;
+            return Error::None;
+        }
+    }
+    ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId);
+    return Error::BadParameter;
+}
+
+Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
+        uint32_t* outId) const {
+    for (const auto& idPair : mHwc1Ids) {
+        if (mode == idPair.first) {
+            *outId = idPair.second;
+            return Error::None;
+        }
+    }
+    ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId);
+    return Error::BadParameter;
+}
+
+bool HWC2On1Adapter::Display::Config::merge(const Config& other) {
+    auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
+            HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
+            HWC2::Attribute::DpiY};
+    for (auto attribute : attributes) {
+        if (getAttribute(attribute) != other.getAttribute(attribute)) {
+            return false;
+        }
+    }
+    android_color_mode_t otherColorMode =
+            static_cast<android_color_mode_t>(other.getAttribute(ColorMode));
+    if (mHwc1Ids.count(otherColorMode) != 0) {
+        ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
+                "identical", mHwc1Ids.at(otherColorMode),
+                other.mHwc1Ids.at(otherColorMode));
+        return false;
+    }
+    mHwc1Ids.emplace(otherColorMode,
+            other.mHwc1Ids.at(otherColorMode));
+    return true;
+}
+
+std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const {
+    std::set<android_color_mode_t> colorModes;
+    for (const auto& idPair : mHwc1Ids) {
+        colorModes.emplace(idPair.first);
+    }
+    return colorModes;
+}
+
+std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const {
+    std::string output;
+
+    const size_t BUFFER_SIZE = 100;
+    char buffer[BUFFER_SIZE] = {};
+    auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
+            "%u x %u", mAttributes.at(HWC2::Attribute::Width),
+            mAttributes.at(HWC2::Attribute::Height));
+    output.append(buffer, writtenBytes);
+
+    if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) {
+        std::memset(buffer, 0, BUFFER_SIZE);
+        writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz",
+                1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod));
+        output.append(buffer, writtenBytes);
+    }
+
+    if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
+            mAttributes.at(HWC2::Attribute::DpiX) != -1) {
+        std::memset(buffer, 0, BUFFER_SIZE);
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                ", DPI: %.1f x %.1f",
+                mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
+                mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
+        output.append(buffer, writtenBytes);
+    }
+
+    std::memset(buffer, 0, BUFFER_SIZE);
+    if (splitLine) {
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                "\n        HWC1 ID/Color transform:");
+    } else {
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                ", HWC1 ID/Color transform:");
+    }
+    output.append(buffer, writtenBytes);
+
+
+    for (const auto& id : mHwc1Ids) {
+        android_color_mode_t colorMode = id.first;
+        uint32_t hwc1Id = id.second;
+        std::memset(buffer, 0, BUFFER_SIZE);
+        if (colorMode == mDisplay.mActiveColorMode) {
+            writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
+                    colorMode);
+        } else {
+            writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
+                    colorMode);
+        }
+        output.append(buffer, writtenBytes);
+    }
+
+    return output;
+}
+
+std::shared_ptr<const HWC2On1Adapter::Display::Config>
+        HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const {
+    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+        return nullptr;
+    }
+    return mConfigs[configId];
+}
+
+void HWC2On1Adapter::Display::populateColorModes() {
+    mColorModes = mConfigs[0]->getColorModes();
+    for (const auto& config : mConfigs) {
+        std::set<android_color_mode_t> intersection;
+        auto configModes = config->getColorModes();
+        std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
+                configModes.cbegin(), configModes.cend(),
+                std::inserter(intersection, intersection.begin()));
+        std::swap(intersection, mColorModes);
+    }
+}
+
+void HWC2On1Adapter::Display::initializeActiveConfig() {
+    if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
+        ALOGV("getActiveConfig is null, choosing config 0");
+        mActiveConfig = mConfigs[0];
+        mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+        return;
+    }
+
+    auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
+            mDevice.mHwc1Device, mHwc1Id);
+
+    // Some devices startup without an activeConfig:
+    // We need to set one ourselves.
+    if (activeConfig == HWC_ERROR) {
+        ALOGV("There is no active configuration: Picking the first one: 0.");
+        const int defaultIndex = 0;
+        mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex);
+        activeConfig = defaultIndex;
+    }
+
+    for (const auto& config : mConfigs) {
+        if (config->hasHwc1Id(activeConfig)) {
+            ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig);
+            mActiveConfig = config;
+            if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
+                // This should never happen since we checked for the config's presence before
+                // setting it as active.
+                ALOGE("Unable to find color mode for active HWC1 config %d", config->getId());
+                mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+            }
+            break;
+        }
+    }
+    if (!mActiveConfig) {
+        ALOGV("Unable to find active HWC1 config %u, defaulting to "
+                "config 0", activeConfig);
+        mActiveConfig = mConfigs[0];
+        mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+    }
+
+
+
+
+}
+
+void HWC2On1Adapter::Display::allocateRequestedContents() {
+    // What needs to be allocated:
+    // 1 hwc_display_contents_1_t
+    // 1 hwc_layer_1_t for each layer
+    // 1 hwc_rect_t for each layer's surfaceDamage
+    // 1 hwc_rect_t for each layer's visibleRegion
+    // 1 hwc_layer_1_t for the framebuffer
+    // 1 hwc_rect_t for the framebuffer's visibleRegion
+
+    // Count # of surfaceDamage
+    size_t numSurfaceDamages = 0;
+    for (const auto& layer : mLayers) {
+        numSurfaceDamages += layer->getNumSurfaceDamages();
+    }
+
+    // Count # of visibleRegions (start at 1 for mandatory framebuffer target
+    // region)
+    size_t numVisibleRegion = 1;
+    for (const auto& layer : mLayers) {
+        numVisibleRegion += layer->getNumVisibleRegions();
+    }
+
+    size_t numRects = numVisibleRegion + numSurfaceDamages;
+    auto numLayers = mLayers.size() + 1;
+    size_t size = sizeof(hwc_display_contents_1_t) +
+            sizeof(hwc_layer_1_t) * numLayers +
+            sizeof(hwc_rect_t) * numRects;
+    auto contents = static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
+    mHwc1RequestedContents.reset(contents);
+    mNextAvailableRect = reinterpret_cast<hwc_rect_t*>(&contents->hwLayers[numLayers]);
+    mNumAvailableRects = numRects;
+}
+
+void HWC2On1Adapter::Display::assignHwc1LayerIds() {
+    mHwc1LayerMap.clear();
+    size_t nextHwc1Id = 0;
+    for (auto& layer : mLayers) {
+        mHwc1LayerMap[nextHwc1Id] = layer;
+        layer->setHwc1Id(nextHwc1Id++);
+    }
+}
+
+void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
+        const Layer& layer) {
+    auto layerId = layer.getId();
+    switch (hwc1Layer.compositionType) {
+        case HWC_FRAMEBUFFER:
+            if (layer.getCompositionType() != Composition::Client) {
+                mChanges->addTypeChange(layerId, Composition::Client);
+            }
+            break;
+        case HWC_OVERLAY:
+            if (layer.getCompositionType() != Composition::Device) {
+                mChanges->addTypeChange(layerId, Composition::Device);
+            }
+            break;
+        case HWC_BACKGROUND:
+            ALOGE_IF(layer.getCompositionType() != Composition::SolidColor,
+                    "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2"
+                    " wasn't expecting SolidColor");
+            break;
+        case HWC_FRAMEBUFFER_TARGET:
+            // Do nothing, since it shouldn't be modified by HWC1
+            break;
+        case HWC_SIDEBAND:
+            ALOGE_IF(layer.getCompositionType() != Composition::Sideband,
+                    "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2"
+                    " wasn't expecting Sideband");
+            break;
+        case HWC_CURSOR_OVERLAY:
+            ALOGE_IF(layer.getCompositionType() != Composition::Cursor,
+                    "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but"
+                    " HWC2 wasn't expecting Cursor");
+            break;
+    }
+}
+
+void HWC2On1Adapter::Display::updateLayerRequests(
+        const hwc_layer_1_t& hwc1Layer, const Layer& layer) {
+    if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
+        mChanges->addLayerRequest(layer.getId(),
+                LayerRequest::ClearClientTarget);
+    }
+}
+
+void HWC2On1Adapter::Display::prepareFramebufferTarget() {
+    // We check that mActiveConfig is valid in Display::prepare
+    int32_t width = mActiveConfig->getAttribute(Attribute::Width);
+    int32_t height = mActiveConfig->getAttribute(Attribute::Height);
+
+    auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()];
+    hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET;
+    hwc1Target.releaseFenceFd = -1;
+    hwc1Target.hints = 0;
+    hwc1Target.flags = 0;
+    hwc1Target.transform = 0;
+    hwc1Target.blending = HWC_BLENDING_PREMULT;
+    if (mDevice.getHwc1MinorVersion() < 3) {
+        hwc1Target.sourceCropi = {0, 0, width, height};
+    } else {
+        hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width),
+                static_cast<float>(height)};
+    }
+    hwc1Target.displayFrame = {0, 0, width, height};
+    hwc1Target.planeAlpha = 255;
+
+    hwc1Target.visibleRegionScreen.numRects = 1;
+    hwc_rect_t* rects = GetRects(1);
+    rects[0].left = 0;
+    rects[0].top = 0;
+    rects[0].right = width;
+    rects[0].bottom = height;
+    hwc1Target.visibleRegionScreen.rects = rects;
+
+    // We will set this to the correct value in set
+    hwc1Target.acquireFenceFd = -1;
+}
+
+// Layer functions
+
+std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1);
+
+HWC2On1Adapter::Layer::Layer(Display& display)
+  : mId(sNextId++),
+    mDisplay(display),
+    mBuffer(),
+    mSurfaceDamage(),
+    mBlendMode(BlendMode::None),
+    mColor({0, 0, 0, 0}),
+    mCompositionType(Composition::Invalid),
+    mDisplayFrame({0, 0, -1, -1}),
+    mPlaneAlpha(0.0f),
+    mSidebandStream(nullptr),
+    mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}),
+    mTransform(Transform::None),
+    mVisibleRegion(),
+    mZ(0),
+    mReleaseFence(),
+    mHwc1Id(0),
+    mHasUnsupportedPlaneAlpha(false) {}
+
+bool HWC2On1Adapter::SortLayersByZ::operator()(
+        const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) {
+    return lhs->getZ() < rhs->getZ();
+}
+
+Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
+        int32_t acquireFence) {
+    ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
+    mBuffer.setBuffer(buffer);
+    mBuffer.setFence(acquireFence);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) {
+    if (mCompositionType != Composition::Cursor) {
+        return Error::BadLayer;
+    }
+
+    if (mDisplay.hasChanges()) {
+        return Error::NotValidated;
+    }
+
+    auto displayId = mDisplay.getHwc1Id();
+    auto hwc1Device = mDisplay.getDevice().getHwc1Device();
+    hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) {
+    // HWC1 supports surface damage starting only with version 1.5.
+    if (mDisplay.getDevice().mHwc1MinorVersion < 5) {
+        return Error::None;
+    }
+    mSurfaceDamage.resize(damage.numRects);
+    std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
+    return Error::None;
+}
+
+// Layer state functions
+
+Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) {
+    mBlendMode = mode;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) {
+    mColor = color;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCompositionType(Composition type) {
+    mCompositionType = type;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) {
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) {
+    mDisplayFrame = frame;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) {
+    mPlaneAlpha = alpha;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) {
+    mSidebandStream = stream;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) {
+    mSourceCrop = crop;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setTransform(Transform transform) {
+    mTransform = transform;
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
+    mVisibleRegion.resize(visible.numRects);
+    std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+    mDisplay.markGeometryChanged();
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setZ(uint32_t z) {
+    mZ = z;
+    return Error::None;
+}
+
+void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) {
+    ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
+    mReleaseFence.add(fenceFd);
+}
+
+const sp<MiniFence>& HWC2On1Adapter::Layer::getReleaseFence() const {
+    return mReleaseFence.get();
+}
+
+void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) {
+    applyCommonState(hwc1Layer);
+    applyCompositionType(hwc1Layer);
+    switch (mCompositionType) {
+        case Composition::SolidColor : applySolidColorState(hwc1Layer); break;
+        case Composition::Sideband : applySidebandState(hwc1Layer); break;
+        default: applyBufferState(hwc1Layer); break;
+    }
+}
+
+static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
+        const std::vector<hwc_rect_t>& surfaceDamage) {
+    std::string regions;
+    regions += "        Visible Region";
+    regions.resize(40, ' ');
+    regions += "Surface Damage\n";
+
+    size_t numPrinted = 0;
+    size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size());
+    while (numPrinted < maxSize) {
+        std::string line("        ");
+        if (visibleRegion.empty() && numPrinted == 0) {
+            line += "None";
+        } else if (numPrinted < visibleRegion.size()) {
+            line += rectString(visibleRegion[numPrinted]);
+        }
+        line.resize(40, ' ');
+        if (surfaceDamage.empty() && numPrinted == 0) {
+            line += "None";
+        } else if (numPrinted < surfaceDamage.size()) {
+            line += rectString(surfaceDamage[numPrinted]);
+        }
+        line += '\n';
+        regions += line;
+        ++numPrinted;
+    }
+    return regions;
+}
+
+std::string HWC2On1Adapter::Layer::dump() const {
+    std::stringstream output;
+    const char* fill = "      ";
+
+    output << fill << to_string(mCompositionType);
+    output << " Layer  HWC2/1: " << mId << "/" << mHwc1Id << "  ";
+    output << "Z: " << mZ;
+    if (mCompositionType == HWC2::Composition::SolidColor) {
+        output << "  " << colorString(mColor);
+    } else if (mCompositionType == HWC2::Composition::Sideband) {
+        output << "  Handle: " << mSidebandStream << '\n';
+    } else {
+        output << "  Buffer: " << mBuffer.getBuffer() << "/" <<
+                mBuffer.getFence() << '\n';
+        output << fill << "  Display frame [LTRB]: " <<
+                rectString(mDisplayFrame) << '\n';
+        output << fill << "  Source crop: " <<
+                frectString(mSourceCrop) << '\n';
+        output << fill << "  Transform: " << to_string(mTransform);
+        output << "  Blend mode: " << to_string(mBlendMode);
+        if (mPlaneAlpha != 1.0f) {
+            output << "  Alpha: " <<
+                alphaString(mPlaneAlpha) << '\n';
+        } else {
+            output << '\n';
+        }
+        output << regionStrings(mVisibleRegion, mSurfaceDamage);
+    }
+    return output.str();
+}
+
+static int getHwc1Blending(HWC2::BlendMode blendMode) {
+    switch (blendMode) {
+        case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
+        case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
+        default: return HWC_BLENDING_NONE;
+    }
+}
+
+void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) {
+    auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
+    hwc1Layer.blending = getHwc1Blending(mBlendMode);
+    hwc1Layer.displayFrame = mDisplayFrame;
+
+    auto pendingAlpha = mPlaneAlpha;
+    if (minorVersion < 2) {
+        mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
+    } else {
+        hwc1Layer.planeAlpha =
+                static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
+    }
+
+    if (minorVersion < 3) {
+        auto pending = mSourceCrop;
+        hwc1Layer.sourceCropi.left =
+                static_cast<int32_t>(std::ceil(pending.left));
+        hwc1Layer.sourceCropi.top =
+                static_cast<int32_t>(std::ceil(pending.top));
+        hwc1Layer.sourceCropi.right =
+                static_cast<int32_t>(std::floor(pending.right));
+        hwc1Layer.sourceCropi.bottom =
+                static_cast<int32_t>(std::floor(pending.bottom));
+    } else {
+        hwc1Layer.sourceCropf = mSourceCrop;
+    }
+
+    hwc1Layer.transform = static_cast<uint32_t>(mTransform);
+
+    auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+    hwc1VisibleRegion.numRects = mVisibleRegion.size();
+    hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects);
+    hwc1VisibleRegion.rects = rects;
+    for (size_t i = 0; i < mVisibleRegion.size(); i++) {
+        rects[i] = mVisibleRegion[i];
+    }
+}
+
+void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) {
+    // If the device does not support background color it is likely to make
+    // assumption regarding backgroundColor and handle (both fields occupy
+    // the same location in hwc_layer_1_t union).
+    // To not confuse these devices we don't set background color and we
+    // make sure handle is a null pointer.
+    if (hasUnsupportedBackgroundColor()) {
+        hwc1Layer.handle = nullptr;
+    } else {
+        hwc1Layer.backgroundColor = mColor;
+    }
+}
+
+void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) {
+    hwc1Layer.sidebandStream = mSidebandStream;
+}
+
+void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) {
+    hwc1Layer.handle = mBuffer.getBuffer();
+    hwc1Layer.acquireFenceFd = mBuffer.getFence();
+}
+
+void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) {
+    // HWC1 never supports color transforms or dataspaces and only sometimes
+    // supports plane alpha (depending on the version). These require us to drop
+    // some or all layers to client composition.
+    if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() ||
+            hasUnsupportedBackgroundColor()) {
+        hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+        hwc1Layer.flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    hwc1Layer.flags = 0;
+    switch (mCompositionType) {
+        case Composition::Client:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
+        case Composition::Device:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            break;
+        case Composition::SolidColor:
+            // In theory the following line should work, but since the HWC1
+            // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
+            // devices may not work correctly. To be on the safe side, we
+            // fall back to client composition.
+            //
+            // hwc1Layer.compositionType = HWC_BACKGROUND;
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
+        case Composition::Cursor:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
+                hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
+            }
+            break;
+        case Composition::Sideband:
+            if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
+                hwc1Layer.compositionType = HWC_SIDEBAND;
+            } else {
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                hwc1Layer.flags |= HWC_SKIP_LAYER;
+            }
+            break;
+        default:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
+    }
+    ALOGV("Layer %" PRIu64 " %s set to %d", mId,
+            to_string(mCompositionType).c_str(),
+            hwc1Layer.compositionType);
+    ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, "    and skipping");
+}
+
+// Adapter helpers
+
+void HWC2On1Adapter::populateCapabilities() {
+    if (mHwc1MinorVersion >= 3U) {
+        int supportedTypes = 0;
+        auto result = mHwc1Device->query(mHwc1Device,
+                HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
+        if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
+            ALOGI("Found support for HWC virtual displays");
+            mHwc1SupportsVirtualDisplays = true;
+        }
+    }
+    if (mHwc1MinorVersion >= 4U) {
+        mCapabilities.insert(Capability::SidebandStream);
+    }
+
+    // Check for HWC background color layer support.
+    if (mHwc1MinorVersion >= 1U) {
+        int backgroundColorSupported = 0;
+        auto result = mHwc1Device->query(mHwc1Device,
+                                         HWC_BACKGROUND_LAYER_SUPPORTED,
+                                         &backgroundColorSupported);
+        if ((result == 0) && (backgroundColorSupported == 1)) {
+            ALOGV("Found support for HWC background color");
+            mHwc1SupportsBackgroundColor = true;
+        }
+    }
+
+    // Some devices might have HWC1 retire fences that accurately emulate
+    // HWC2 present fences when they are deferred, but it's not very reliable.
+    // To be safe, we indicate PresentFenceIsNotReliable for all HWC1 devices.
+    mCapabilities.insert(Capability::PresentFenceIsNotReliable);
+}
+
+HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) {
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    auto display = mDisplays.find(id);
+    if (display == mDisplays.end()) {
+        return nullptr;
+    }
+
+    return display->second.get();
+}
+
+std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
+        hwc2_display_t displayId, hwc2_layer_t layerId) {
+    auto display = getDisplay(displayId);
+    if (!display) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
+    }
+
+    auto layerEntry = mLayers.find(layerId);
+    if (layerEntry == mLayers.end()) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+    }
+
+    auto layer = layerEntry->second;
+    if (layer->getDisplay().getId() != displayId) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+    }
+    return std::make_tuple(layer.get(), Error::None);
+}
+
+void HWC2On1Adapter::populatePrimary() {
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+    mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
+    display->setHwc1Id(HWC_DISPLAY_PRIMARY);
+    display->populateConfigs();
+    mDisplays.emplace(display->getId(), std::move(display));
+}
+
+bool HWC2On1Adapter::prepareAllDisplays() {
+    ATRACE_CALL();
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    for (const auto& displayPair : mDisplays) {
+        auto& display = displayPair.second;
+        if (!display->prepare()) {
+            return false;
+        }
+    }
+
+    if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) {
+        ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
+        return false;
+    }
+
+    // Build an array of hwc_display_contents_1 to call prepare() on HWC1.
+    mHwc1Contents.clear();
+
+    // Always push the primary display
+    auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
+    auto& primaryDisplay = mDisplays[primaryDisplayId];
+    mHwc1Contents.push_back(primaryDisplay->getDisplayContents());
+
+    // Push the external display, if present
+    if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
+        auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
+        auto& externalDisplay = mDisplays[externalDisplayId];
+        mHwc1Contents.push_back(externalDisplay->getDisplayContents());
+    } else {
+        // Even if an external display isn't present, we still need to send
+        // at least two displays down to HWC1
+        mHwc1Contents.push_back(nullptr);
+    }
+
+    // Push the hardware virtual display, if supported and present
+    if (mHwc1MinorVersion >= 3) {
+        if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
+            auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
+            auto& virtualDisplay = mDisplays[virtualDisplayId];
+            mHwc1Contents.push_back(virtualDisplay->getDisplayContents());
+        } else {
+            mHwc1Contents.push_back(nullptr);
+        }
+    }
+
+    for (auto& displayContents : mHwc1Contents) {
+        if (!displayContents) {
+            continue;
+        }
+
+        ALOGV("Display %zd layers:", mHwc1Contents.size() - 1);
+        for (size_t l = 0; l < displayContents->numHwLayers; ++l) {
+            auto& layer = displayContents->hwLayers[l];
+            ALOGV("  %zd: %d", l, layer.compositionType);
+        }
+    }
+
+    ALOGV("Calling HWC1 prepare");
+    {
+        ATRACE_NAME("HWC1 prepare");
+        mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(),
+                mHwc1Contents.data());
+    }
+
+    for (size_t c = 0; c < mHwc1Contents.size(); ++c) {
+        auto& contents = mHwc1Contents[c];
+        if (!contents) {
+            continue;
+        }
+        ALOGV("Display %zd layers:", c);
+        for (size_t l = 0; l < contents->numHwLayers; ++l) {
+            ALOGV("  %zd: %d", l, contents->hwLayers[l].compositionType);
+        }
+    }
+
+    // Return the received contents to their respective displays
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        display->generateChanges();
+    }
+
+    return true;
+}
+
+void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays,
+                     hwc_display_contents_1_t** displays) {
+    ALOGV("*****************************");
+    size_t displayId = 0;
+    while (displayId < numDisplays) {
+        hwc_display_contents_1_t* display = displays[displayId];
+
+        ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display);
+        if (display == nullptr) {
+            displayId++;
+            continue;
+        }
+        ALOGV("  retirefd:0x%08x", display->retireFenceFd);
+        ALOGV("  outbuf  :0x%p", display->outbuf);
+        ALOGV("  outbuffd:0x%08x", display->outbufAcquireFenceFd);
+        ALOGV("  flags   :0x%08x", display->flags);
+        for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) {
+            hwc_layer_1_t& layer = display->hwLayers[layerId];
+            ALOGV("    Layer[%zu]:", layerId);
+            ALOGV("      composition        : 0x%08x", layer.compositionType);
+            ALOGV("      hints              : 0x%08x", layer.hints);
+            ALOGV("      flags              : 0x%08x", layer.flags);
+            ALOGV("      handle             : 0x%p", layer.handle);
+            ALOGV("      transform          : 0x%08x", layer.transform);
+            ALOGV("      blending           : 0x%08x", layer.blending);
+            ALOGV("      sourceCropf        : %f, %f, %f, %f",
+                  layer.sourceCropf.left,
+                  layer.sourceCropf.top,
+                  layer.sourceCropf.right,
+                  layer.sourceCropf.bottom);
+            ALOGV("      displayFrame       : %d, %d, %d, %d",
+                  layer.displayFrame.left,
+                  layer.displayFrame.left,
+                  layer.displayFrame.left,
+                  layer.displayFrame.left);
+            hwc_region_t& visReg = layer.visibleRegionScreen;
+            ALOGV("      visibleRegionScreen: #0x%08zx[@0x%p]",
+                  visReg.numRects,
+                  visReg.rects);
+            for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) {
+                if (layer.visibleRegionScreen.rects == nullptr) {
+                    ALOGV("        null");
+                } else {
+                    ALOGV("        visibleRegionScreen[%zu] %d, %d, %d, %d",
+                          visRegId,
+                          visReg.rects[visRegId].left,
+                          visReg.rects[visRegId].top,
+                          visReg.rects[visRegId].right,
+                          visReg.rects[visRegId].bottom);
+                }
+            }
+            ALOGV("      acquireFenceFd     : 0x%08x", layer.acquireFenceFd);
+            ALOGV("      releaseFenceFd     : 0x%08x", layer.releaseFenceFd);
+            ALOGV("      planeAlpha         : 0x%08x", layer.planeAlpha);
+            if (getMinorVersion(device) < 5)
+               continue;
+            ALOGV("      surfaceDamage      : #0x%08zx[@0x%p]",
+                  layer.surfaceDamage.numRects,
+                  layer.surfaceDamage.rects);
+            for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) {
+                if (layer.surfaceDamage.rects == nullptr) {
+                    ALOGV("      null");
+                } else {
+                    ALOGV("      surfaceDamage[%zu] %d, %d, %d, %d",
+                          sdId,
+                          layer.surfaceDamage.rects[sdId].left,
+                          layer.surfaceDamage.rects[sdId].top,
+                          layer.surfaceDamage.rects[sdId].right,
+                          layer.surfaceDamage.rects[sdId].bottom);
+                }
+            }
+        }
+        displayId++;
+    }
+    ALOGV("-----------------------------");
+}
+
+Error HWC2On1Adapter::setAllDisplays() {
+    ATRACE_CALL();
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    // Make sure we're ready to validate
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        Error error = display->set(*mHwc1Contents[hwc1Id]);
+        if (error != Error::None) {
+            ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id,
+                    to_string(error).c_str());
+            return error;
+        }
+    }
+
+    ALOGV("Calling HWC1 set");
+    {
+        ATRACE_NAME("HWC1 set");
+        //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data());
+        mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
+                mHwc1Contents.data());
+    }
+
+    // Add retire and release fences
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd;
+        ALOGV("setAllDisplays: Adding retire fence %d to display %zd",
+                retireFenceFd, hwc1Id);
+        display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd);
+        display->addReleaseFences(*mHwc1Contents[hwc1Id]);
+    }
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::hwc1Invalidate() {
+    ALOGV("Received hwc1Invalidate");
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered.
+    if (mCallbacks.count(Callback::Refresh) == 0) {
+        mHasPendingInvalidate = true;
+        return;
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Refresh];
+    std::vector<hwc2_display_t> displays;
+    for (const auto& displayPair : mDisplays) {
+        displays.emplace_back(displayPair.first);
+    }
+
+    // Call back without the state lock held.
+    lock.unlock();
+
+    auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
+    for (auto display : displays) {
+        refresh(callbackInfo.data, display);
+    }
+}
+
+void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) {
+    ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered.
+    if (mCallbacks.count(Callback::Vsync) == 0) {
+        mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
+        return;
+    }
+
+    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+        ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId);
+        return;
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Vsync];
+    auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+
+    // Call back without the state lock held.
+    lock.unlock();
+
+    auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
+    vsync(callbackInfo.data, displayId, timestamp);
+}
+
+void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
+    ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
+
+    if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
+        ALOGE("hwc1Hotplug: Received hotplug for non-external display");
+        return;
+    }
+
+    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered
+    if (mCallbacks.count(Callback::Hotplug) == 0) {
+        mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
+        return;
+    }
+
+    hwc2_display_t displayId = UINT64_MAX;
+    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+        if (connected == 0) {
+            ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
+            return;
+        }
+
+        // Create a new display on connect
+        auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
+                HWC2::DisplayType::Physical);
+        display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
+        display->populateConfigs();
+        displayId = display->getId();
+        mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
+        mDisplays.emplace(displayId, std::move(display));
+    } else {
+        if (connected != 0) {
+            ALOGW("hwc1Hotplug: Received connect for previously connected "
+                    "display");
+            return;
+        }
+
+        // Disconnect an existing display
+        displayId = mHwc1DisplayMap[hwc1DisplayId];
+        mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
+        mDisplays.erase(displayId);
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Hotplug];
+
+    // Call back without the state lock held
+    lock.unlock();
+
+    auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
+    auto hwc2Connected = (connected == 0) ?
+            HWC2::Connection::Disconnected : HWC2::Connection::Connected;
+    hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
+}
+} // namespace android
diff --git a/libs/hwc2on1adapter/MiniFence.cpp b/libs/hwc2on1adapter/MiniFence.cpp
new file mode 100644
index 0000000..dfbe4d6
--- /dev/null
+++ b/libs/hwc2on1adapter/MiniFence.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hwc2on1adapter/MiniFence.h"
+
+#include <unistd.h>
+
+namespace android {
+
+const sp<MiniFence> MiniFence::NO_FENCE = sp<MiniFence>(new MiniFence);
+
+MiniFence::MiniFence() :
+    mFenceFd(-1) {
+}
+
+MiniFence::MiniFence(int fenceFd) :
+    mFenceFd(fenceFd) {
+}
+
+MiniFence::~MiniFence() {
+    if (mFenceFd != -1) {
+        close(mFenceFd);
+    }
+}
+
+int MiniFence::dup() const {
+    return ::dup(mFenceFd);
+}
+}
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
new file mode 100644
index 0000000..3badfce
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
@@ -0,0 +1,738 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_1_ADAPTER_H
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "MiniFence.h"
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <queue>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+struct hwc_composer_device_1;
+struct hwc_display_contents_1;
+struct hwc_layer_1;
+
+namespace android {
+
+// For devices unable to provide an implementation of HWC2 (see hwcomposer2.h),
+// we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates
+// streamed function calls ala HWC2 model to batched array of structs calls ala
+// HWC1 model.
+class HWC2On1Adapter : public hwc2_device_t
+{
+public:
+    explicit HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device);
+    ~HWC2On1Adapter();
+
+    struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; }
+    uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; }
+
+private:
+    static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) {
+        return static_cast<HWC2On1Adapter*>(device);
+    }
+
+    // getCapabilities
+
+    void doGetCapabilities(uint32_t* outCount,
+            int32_t* /*hwc2_capability_t*/ outCapabilities);
+    static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount,
+            int32_t* /*hwc2_capability_t*/ outCapabilities) {
+        getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
+    }
+
+    bool supportsBackgroundColor() {
+        return mHwc1SupportsBackgroundColor;
+    }
+
+    // getFunction
+
+    hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
+    static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device,
+            int32_t intDesc) {
+        auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc);
+        return getAdapter(device)->doGetFunction(descriptor);
+    }
+
+    // Device functions
+
+    HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height,
+            hwc2_display_t* outDisplay);
+    static int32_t createVirtualDisplayHook(hwc2_device_t* device,
+            uint32_t width, uint32_t height, int32_t* /*format*/,
+            hwc2_display_t* outDisplay) {
+        // HWC1 implementations cannot override the buffer format requested by
+        // the consumer
+        auto error = getAdapter(device)->createVirtualDisplay(width, height,
+                outDisplay);
+        return static_cast<int32_t>(error);
+    }
+
+    HWC2::Error destroyVirtualDisplay(hwc2_display_t display);
+    static int32_t destroyVirtualDisplayHook(hwc2_device_t* device,
+            hwc2_display_t display) {
+        auto error = getAdapter(device)->destroyVirtualDisplay(display);
+        return static_cast<int32_t>(error);
+    }
+
+    std::string mDumpString;
+    void dump(uint32_t* outSize, char* outBuffer);
+    static void dumpHook(hwc2_device_t* device, uint32_t* outSize,
+            char* outBuffer) {
+        getAdapter(device)->dump(outSize, outBuffer);
+    }
+
+    uint32_t getMaxVirtualDisplayCount();
+    static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) {
+        return getAdapter(device)->getMaxVirtualDisplayCount();
+    }
+
+    HWC2::Error registerCallback(HWC2::Callback descriptor,
+            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer);
+    static int32_t registerCallbackHook(hwc2_device_t* device,
+            int32_t intDesc, hwc2_callback_data_t callbackData,
+            hwc2_function_pointer_t pointer) {
+        auto descriptor = static_cast<HWC2::Callback>(intDesc);
+        auto error = getAdapter(device)->registerCallback(descriptor,
+                callbackData, pointer);
+        return static_cast<int32_t>(error);
+    }
+
+    // Display functions
+
+    class Layer;
+
+    class SortLayersByZ {
+        public:
+            bool operator()(const std::shared_ptr<Layer>& lhs,
+                    const std::shared_ptr<Layer>& rhs);
+    };
+
+    // The semantics of the fences returned by the device differ between
+    // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h
+    // for more information.
+    //
+    // Release fences in hwc1 are obtained on set() for a frame n and signaled
+    // when the layer buffer is not needed for read operations anymore
+    // (typically on frame n+1). In HWC2, release fences are obtained with a
+    // special call after present() for frame n. These fences signal
+    // on frame n: More specifically, the fence for a given buffer provided in
+    // frame n will signal when the prior buffer is no longer required.
+    //
+    // A retire fence (HWC1) is signaled when a composition is replaced
+    // on the panel whereas a present fence (HWC2) is signaled when a
+    // composition starts to be displayed on a panel.
+    //
+    // The HWC2to1Adapter emulates the new fence semantics for a frame
+    // n by returning the fence from frame n-1. For frame 0, the adapter
+    // returns NO_FENCE.
+    class DeferredFence {
+        public:
+            DeferredFence()
+              : mFences({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {}
+
+            void add(int32_t fenceFd) {
+                mFences.emplace(new MiniFence(fenceFd));
+                mFences.pop();
+            }
+
+            const sp<MiniFence>& get() const {
+                return mFences.front();
+            }
+
+        private:
+            // There are always two fences in this queue.
+            std::queue<sp<MiniFence>> mFences;
+    };
+
+    class FencedBuffer {
+        public:
+            FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {}
+
+            void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
+            void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); }
+
+            buffer_handle_t getBuffer() const { return mBuffer; }
+            int getFence() const { return mFence->dup(); }
+
+        private:
+            buffer_handle_t mBuffer;
+            sp<MiniFence> mFence;
+    };
+
+    class Display {
+        public:
+            Display(HWC2On1Adapter& device, HWC2::DisplayType type);
+
+            hwc2_display_t getId() const { return mId; }
+            HWC2On1Adapter& getDevice() const { return mDevice; }
+
+            // Does not require locking because it is set before adding the
+            // Displays to the Adapter's list of displays
+            void setHwc1Id(int32_t id) { mHwc1Id = id; }
+            int32_t getHwc1Id() const { return mHwc1Id; }
+
+            // HWC2 Display functions
+            HWC2::Error acceptChanges();
+            HWC2::Error createLayer(hwc2_layer_t* outLayerId);
+            HWC2::Error destroyLayer(hwc2_layer_t layerId);
+            HWC2::Error getActiveConfig(hwc2_config_t* outConfigId);
+            HWC2::Error getAttribute(hwc2_config_t configId,
+                    HWC2::Attribute attribute, int32_t* outValue);
+            HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
+                    hwc2_layer_t* outLayers, int32_t* outTypes);
+            HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes);
+            HWC2::Error getConfigs(uint32_t* outNumConfigs,
+                    hwc2_config_t* outConfigIds);
+            HWC2::Error getDozeSupport(int32_t* outSupport);
+            HWC2::Error getHdrCapabilities(uint32_t* outNumTypes,
+                    int32_t* outTypes, float* outMaxLuminance,
+                    float* outMaxAverageLuminance, float* outMinLuminance);
+            HWC2::Error getName(uint32_t* outSize, char* outName);
+            HWC2::Error getReleaseFences(uint32_t* outNumElements,
+                    hwc2_layer_t* outLayers, int32_t* outFences);
+            HWC2::Error getRequests(int32_t* outDisplayRequests,
+                    uint32_t* outNumElements, hwc2_layer_t* outLayers,
+                    int32_t* outLayerRequests);
+            HWC2::Error getType(int32_t* outType);
+
+            // Since HWC1 "presents" (called "set" in HWC1) all Displays
+            // at once, the first call to any Display::present will trigger
+            // present() on all Displays in the Device. Subsequent calls without
+            // first calling validate() are noop (except for duping/returning
+            // the retire fence).
+            HWC2::Error present(int32_t* outRetireFence);
+
+            HWC2::Error setActiveConfig(hwc2_config_t configId);
+            HWC2::Error setClientTarget(buffer_handle_t target,
+                    int32_t acquireFence, int32_t dataspace,
+                    hwc_region_t damage);
+            HWC2::Error setColorMode(android_color_mode_t mode);
+            HWC2::Error setColorTransform(android_color_transform_t hint);
+            HWC2::Error setOutputBuffer(buffer_handle_t buffer,
+                    int32_t releaseFence);
+            HWC2::Error setPowerMode(HWC2::PowerMode mode);
+            HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
+
+            // Since HWC1 "validates" (called "prepare" in HWC1) all Displays
+            // at once, the first call to any Display::validate() will trigger
+            // validate() on all other Displays in the Device.
+            HWC2::Error validate(uint32_t* outNumTypes,
+                    uint32_t* outNumRequests);
+
+            HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z);
+
+            HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height,
+                     int32_t format, int32_t dataspace);
+
+            // Read configs from HWC1 device
+            void populateConfigs();
+
+            // Set configs for a virtual display
+            void populateConfigs(uint32_t width, uint32_t height);
+
+            bool prepare();
+
+            // Called after hwc.prepare() with responses from the device.
+            void generateChanges();
+
+            bool hasChanges() const;
+            HWC2::Error set(hwc_display_contents_1& hwcContents);
+            void addRetireFence(int fenceFd);
+            void addReleaseFences(const hwc_display_contents_1& hwcContents);
+
+            bool hasColorTransform() const;
+
+            std::string dump() const;
+
+            // Return a rect from the pool allocated during validate()
+            hwc_rect_t* GetRects(size_t numRects);
+
+            hwc_display_contents_1* getDisplayContents();
+
+            void markGeometryChanged() { mGeometryChanged = true; }
+            void resetGeometryMarker() { mGeometryChanged = false;}
+        private:
+            class Config {
+                public:
+                    Config(Display& display)
+                      : mDisplay(display),
+                        mId(0),
+                        mAttributes() {}
+
+                    bool isOnDisplay(const Display& display) const {
+                        return display.getId() == mDisplay.getId();
+                    }
+
+                    void setAttribute(HWC2::Attribute attribute, int32_t value);
+                    int32_t getAttribute(HWC2::Attribute attribute) const;
+
+                    void setHwc1Id(uint32_t id);
+                    bool hasHwc1Id(uint32_t id) const;
+                    HWC2::Error getColorModeForHwc1Id(uint32_t id,
+                            android_color_mode_t *outMode) const;
+                    HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode,
+                            uint32_t* outId) const;
+
+                    void setId(hwc2_config_t id) { mId = id; }
+                    hwc2_config_t getId() const { return mId; }
+
+                    // Attempts to merge two configs that differ only in color
+                    // mode. Returns whether the merge was successful
+                    bool merge(const Config& other);
+
+                    std::set<android_color_mode_t> getColorModes() const;
+
+                    // splitLine divides the output into two lines suitable for
+                    // dumpsys SurfaceFlinger
+                    std::string toString(bool splitLine = false) const;
+
+                private:
+                    Display& mDisplay;
+                    hwc2_config_t mId;
+                    std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
+
+                    // Maps from color transform to HWC1 config ID
+                    std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
+            };
+
+            // Stores changes requested from the device upon calling prepare().
+            // Handles change request to:
+            //   - Layer composition type.
+            //   - Layer hints.
+            class Changes {
+                public:
+                    uint32_t getNumTypes() const {
+                        return static_cast<uint32_t>(mTypeChanges.size());
+                    }
+
+                    uint32_t getNumLayerRequests() const {
+                        return static_cast<uint32_t>(mLayerRequests.size());
+                    }
+
+                    const std::unordered_map<hwc2_layer_t, HWC2::Composition>&
+                            getTypeChanges() const {
+                        return mTypeChanges;
+                    }
+
+                    const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>&
+                            getLayerRequests() const {
+                        return mLayerRequests;
+                    }
+
+                    void addTypeChange(hwc2_layer_t layerId,
+                            HWC2::Composition type) {
+                        mTypeChanges.insert({layerId, type});
+                    }
+
+                    void clearTypeChanges() { mTypeChanges.clear(); }
+
+                    void addLayerRequest(hwc2_layer_t layerId,
+                            HWC2::LayerRequest request) {
+                        mLayerRequests.insert({layerId, request});
+                    }
+
+                private:
+                    std::unordered_map<hwc2_layer_t, HWC2::Composition>
+                            mTypeChanges;
+                    std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
+                            mLayerRequests;
+            };
+
+            std::shared_ptr<const Config>
+                    getConfig(hwc2_config_t configId) const;
+
+            void populateColorModes();
+            void initializeActiveConfig();
+
+            // Creates a bi-directional mapping between index in HWC1
+            // prepare/set array and Layer object. Stores mapping in
+            // mHwc1LayerMap and also updates Layer's attribute mHwc1Id.
+            void assignHwc1LayerIds();
+
+            // Called after a response to prepare() has been received:
+            // Ingest composition type changes requested by the device.
+            void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
+                    const Layer& layer);
+
+            // Called after a response to prepare() has been received:
+            // Ingest layer hint changes requested by the device.
+            void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
+                    const Layer& layer);
+
+            // Set all fields in HWC1 comm array for layer containing the
+            // HWC_FRAMEBUFFER_TARGET (always the last layer).
+            void prepareFramebufferTarget();
+
+            // Display ID generator.
+            static std::atomic<hwc2_display_t> sNextId;
+            const hwc2_display_t mId;
+
+
+            HWC2On1Adapter& mDevice;
+
+            // The state of this display should only be modified from
+            // SurfaceFlinger's main loop, with the exception of when dump is
+            // called. To prevent a bad state from crashing us during a dump
+            // call, all public calls into Display must acquire this mutex.
+            //
+            // It is recursive because we don't want to deadlock in validate
+            // (or present) when we call HWC2On1Adapter::prepareAllDisplays
+            // (or setAllDisplays), which calls back into Display functions
+            // which require locking.
+            mutable std::recursive_mutex mStateMutex;
+
+            // Allocate RAM able to store all layers and rects used for
+            // communication with HWC1. Place allocated RAM in variable
+            // mHwc1RequestedContents.
+            void allocateRequestedContents();
+
+            // Array of structs exchanged between client and hwc1 device.
+            // Sent to device upon calling prepare().
+            std::unique_ptr<hwc_display_contents_1> mHwc1RequestedContents;
+    private:
+            DeferredFence mRetireFence;
+
+            // Will only be non-null after the Display has been validated and
+            // before it has been presented
+            std::unique_ptr<Changes> mChanges;
+
+            int32_t mHwc1Id;
+
+            std::vector<std::shared_ptr<Config>> mConfigs;
+            std::shared_ptr<const Config> mActiveConfig;
+            std::set<android_color_mode_t> mColorModes;
+            android_color_mode_t mActiveColorMode;
+            std::string mName;
+            HWC2::DisplayType mType;
+            HWC2::PowerMode mPowerMode;
+            HWC2::Vsync mVsyncEnabled;
+
+            // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer
+            FencedBuffer mClientTarget;
+
+
+            FencedBuffer mOutputBuffer;
+
+            bool mHasColorTransform;
+
+            // All layers this Display is aware of.
+            std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
+
+            // Mapping between layer index in array of hwc_display_contents_1*
+            // passed to HWC1 during validate/set and Layer object.
+            std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
+
+            // All communication with HWC1 via prepare/set is done with one
+            // alloc. This pointer is pointing to a pool of hwc_rect_t.
+            size_t mNumAvailableRects;
+            hwc_rect_t* mNextAvailableRect;
+
+            // True if any of the Layers contained in this Display have been
+            // updated with anything other than a buffer since last call to
+            // Display::set()
+            bool mGeometryChanged;
+    };
+
+    // Utility template calling a Display object method directly based on the
+    // hwc2_display_t displayId parameter.
+    template <typename ...Args>
+    static int32_t callDisplayFunction(hwc2_device_t* device,
+            hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
+            Args... args) {
+        auto display = getAdapter(device)->getDisplay(displayId);
+        if (!display) {
+            return static_cast<int32_t>(HWC2::Error::BadDisplay);
+        }
+        auto error = ((*display).*member)(std::forward<Args>(args)...);
+        return static_cast<int32_t>(error);
+    }
+
+    template <typename MF, MF memFunc, typename ...Args>
+    static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId,
+            Args... args) {
+        return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc,
+                std::forward<Args>(args)...);
+    }
+
+    static int32_t getDisplayAttributeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_config_t config,
+            int32_t intAttribute, int32_t* outValue) {
+        auto attribute = static_cast<HWC2::Attribute>(intAttribute);
+        return callDisplayFunction(device, display, &Display::getAttribute,
+                config, attribute, outValue);
+    }
+
+    static int32_t setColorTransformHook(hwc2_device_t* device,
+            hwc2_display_t display, const float* /*matrix*/,
+            int32_t /*android_color_transform_t*/ intHint) {
+        // We intentionally throw away the matrix, because if the hint is
+        // anything other than IDENTITY, we have to fall back to client
+        // composition anyway
+        auto hint = static_cast<android_color_transform_t>(intHint);
+        return callDisplayFunction(device, display, &Display::setColorTransform,
+                hint);
+    }
+
+    static int32_t setColorModeHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
+        auto mode = static_cast<android_color_mode_t>(intMode);
+        return callDisplayFunction(device, display, &Display::setColorMode,
+                mode);
+    }
+
+    static int32_t setPowerModeHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t intMode) {
+        auto mode = static_cast<HWC2::PowerMode>(intMode);
+        return callDisplayFunction(device, display, &Display::setPowerMode,
+                mode);
+    }
+
+    static int32_t setVsyncEnabledHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t intEnabled) {
+        auto enabled = static_cast<HWC2::Vsync>(intEnabled);
+        return callDisplayFunction(device, display, &Display::setVsyncEnabled,
+                enabled);
+    }
+
+    class Layer {
+        public:
+            explicit Layer(Display& display);
+
+            bool operator==(const Layer& other) { return mId == other.mId; }
+            bool operator!=(const Layer& other) { return !(*this == other); }
+
+            hwc2_layer_t getId() const { return mId; }
+            Display& getDisplay() const { return mDisplay; }
+
+            // HWC2 Layer functions
+            HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
+            HWC2::Error setCursorPosition(int32_t x, int32_t y);
+            HWC2::Error setSurfaceDamage(hwc_region_t damage);
+
+            // HWC2 Layer state functions
+            HWC2::Error setBlendMode(HWC2::BlendMode mode);
+            HWC2::Error setColor(hwc_color_t color);
+            HWC2::Error setCompositionType(HWC2::Composition type);
+            HWC2::Error setDataspace(android_dataspace_t dataspace);
+            HWC2::Error setDisplayFrame(hwc_rect_t frame);
+            HWC2::Error setPlaneAlpha(float alpha);
+            HWC2::Error setSidebandStream(const native_handle_t* stream);
+            HWC2::Error setSourceCrop(hwc_frect_t crop);
+            HWC2::Error setTransform(HWC2::Transform transform);
+            HWC2::Error setVisibleRegion(hwc_region_t visible);
+            HWC2::Error setZ(uint32_t z);
+
+            HWC2::Composition getCompositionType() const {
+                return mCompositionType;
+            }
+            uint32_t getZ() const { return mZ; }
+
+            void addReleaseFence(int fenceFd);
+            const sp<MiniFence>& getReleaseFence() const;
+
+            void setHwc1Id(size_t id) { mHwc1Id = id; }
+            size_t getHwc1Id() const { return mHwc1Id; }
+
+            // Write state to HWC1 communication struct.
+            void applyState(struct hwc_layer_1& hwc1Layer);
+
+            std::string dump() const;
+
+            std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); }
+
+            std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); }
+
+            // True if a layer cannot be properly rendered by the device due
+            // to usage of SolidColor (a.k.a BackgroundColor in HWC1).
+            bool hasUnsupportedBackgroundColor() {
+                return (mCompositionType == HWC2::Composition::SolidColor &&
+                        !mDisplay.getDevice().supportsBackgroundColor());
+            }
+        private:
+            void applyCommonState(struct hwc_layer_1& hwc1Layer);
+            void applySolidColorState(struct hwc_layer_1& hwc1Layer);
+            void applySidebandState(struct hwc_layer_1& hwc1Layer);
+            void applyBufferState(struct hwc_layer_1& hwc1Layer);
+            void applyCompositionType(struct hwc_layer_1& hwc1Layer);
+
+            static std::atomic<hwc2_layer_t> sNextId;
+            const hwc2_layer_t mId;
+            Display& mDisplay;
+
+            FencedBuffer mBuffer;
+            std::vector<hwc_rect_t> mSurfaceDamage;
+
+            HWC2::BlendMode mBlendMode;
+            hwc_color_t mColor;
+            HWC2::Composition mCompositionType;
+            hwc_rect_t mDisplayFrame;
+            float mPlaneAlpha;
+            const native_handle_t* mSidebandStream;
+            hwc_frect_t mSourceCrop;
+            HWC2::Transform mTransform;
+            std::vector<hwc_rect_t> mVisibleRegion;
+
+            uint32_t mZ;
+
+            DeferredFence mReleaseFence;
+
+            size_t mHwc1Id;
+            bool mHasUnsupportedPlaneAlpha;
+    };
+
+    // Utility tempate calling a Layer object method based on ID parameters:
+    // hwc2_display_t displayId
+    // and
+    // hwc2_layer_t layerId
+    template <typename ...Args>
+    static int32_t callLayerFunction(hwc2_device_t* device,
+            hwc2_display_t displayId, hwc2_layer_t layerId,
+            HWC2::Error (Layer::*member)(Args...), Args... args) {
+        auto result = getAdapter(device)->getLayer(displayId, layerId);
+        auto error = std::get<HWC2::Error>(result);
+        if (error == HWC2::Error::None) {
+            auto layer = std::get<Layer*>(result);
+            error = ((*layer).*member)(std::forward<Args>(args)...);
+        }
+        return static_cast<int32_t>(error);
+    }
+
+    template <typename MF, MF memFunc, typename ...Args>
+    static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId,
+            hwc2_layer_t layerId, Args... args) {
+        return HWC2On1Adapter::callLayerFunction(device, displayId, layerId,
+                memFunc, std::forward<Args>(args)...);
+    }
+
+    // Layer state functions
+
+    static int32_t setLayerBlendModeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) {
+        auto mode = static_cast<HWC2::BlendMode>(intMode);
+        return callLayerFunction(device, display, layer,
+                &Layer::setBlendMode, mode);
+    }
+
+    static int32_t setLayerCompositionTypeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intType) {
+        auto type = static_cast<HWC2::Composition>(intType);
+        return callLayerFunction(device, display, layer,
+                &Layer::setCompositionType, type);
+    }
+
+    static int32_t setLayerDataspaceHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) {
+        auto dataspace = static_cast<android_dataspace_t>(intDataspace);
+        return callLayerFunction(device, display, layer, &Layer::setDataspace,
+                dataspace);
+    }
+
+    static int32_t setLayerTransformHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
+        auto transform = static_cast<HWC2::Transform>(intTransform);
+        return callLayerFunction(device, display, layer, &Layer::setTransform,
+                transform);
+    }
+
+    static int32_t setLayerZOrderHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, uint32_t z) {
+        return callDisplayFunction(device, display, &Display::updateLayerZ,
+                layer, z);
+    }
+
+    // Adapter internals
+
+    void populateCapabilities();
+    Display* getDisplay(hwc2_display_t id);
+    std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId,
+            hwc2_layer_t layerId);
+    void populatePrimary();
+
+    bool prepareAllDisplays();
+    std::vector<struct hwc_display_contents_1*> mHwc1Contents;
+    HWC2::Error setAllDisplays();
+
+    // Callbacks
+    void hwc1Invalidate();
+    void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
+    void hwc1Hotplug(int hwc1DisplayId, int connected);
+
+    // These are set in the constructor and before any asynchronous events are
+    // possible
+
+    struct hwc_composer_device_1* const mHwc1Device;
+    const uint8_t mHwc1MinorVersion;
+    bool mHwc1SupportsVirtualDisplays;
+    bool mHwc1SupportsBackgroundColor;
+
+    class Callbacks;
+    const std::unique_ptr<Callbacks> mHwc1Callbacks;
+
+    std::unordered_set<HWC2::Capability> mCapabilities;
+
+    // These are only accessed from the main SurfaceFlinger thread (not from
+    // callbacks or dump
+
+    std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
+
+    // A HWC1 supports only one virtual display.
+    std::shared_ptr<Display> mHwc1VirtualDisplay;
+
+    // These are potentially accessed from multiple threads, and are protected
+    // by this mutex. This needs to be recursive, since the HWC1 implementation
+    // can call back into the invalidate callback on the same thread that is
+    // calling prepare.
+    std::recursive_timed_mutex mStateMutex;
+
+    struct CallbackInfo {
+        hwc2_callback_data_t data;
+        hwc2_function_pointer_t pointer;
+    };
+    std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
+    bool mHasPendingInvalidate;
+
+    // There is a small gap between the time the HWC1 module is started and
+    // when the callbacks for vsync and hotplugs are registered by the
+    // HWC2on1Adapter. To prevent losing events they are stored in these arrays
+    // and fed to the callback as soon as possible.
+    std::vector<std::pair<int, int64_t>> mPendingVsyncs;
+    std::vector<std::pair<int, int>> mPendingHotplugs;
+
+    // Mapping between HWC1 display id and Display objects.
+    std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+
+    // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL,
+    // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects.
+    std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
new file mode 100644
index 0000000..75de764
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINIFENCE_H
+#define MINIFENCE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/* MiniFence is a minimal re-implementation of Fence from libui. It exists to
+ * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements.
+ */
+class MiniFence : public LightRefBase<MiniFence> {
+public:
+    static const sp<MiniFence> NO_FENCE;
+
+    // Construct a new MiniFence object with an invalid file descriptor.
+    MiniFence();
+
+    // Construct a new MiniFence object to manage a given fence file descriptor.
+    // When the new MiniFence object is destructed the file descriptor will be
+    // closed.
+    explicit MiniFence(int fenceFd);
+
+    // Not copyable or movable.
+    MiniFence(const MiniFence& rhs) = delete;
+    MiniFence& operator=(const MiniFence& rhs) = delete;
+    MiniFence(MiniFence&& rhs) = delete;
+    MiniFence& operator=(MiniFence&& rhs) = delete;
+
+    // Return a duplicate of the fence file descriptor. The caller is
+    // responsible for closing the returned file descriptor. On error, -1 will
+    // be returned and errno will indicate the problem.
+    int dup() const;
+
+private:
+    // Only allow instantiation using ref counting.
+    friend class LightRefBase<MiniFence>;
+    ~MiniFence();
+
+    int mFenceFd;
+
+};
+}
+#endif //MINIFENCE_H
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
new file mode 100644
index 0000000..9294419
--- /dev/null
+++ b/libs/input/Android.bp
@@ -0,0 +1,67 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// libinput is partially built for the host (used by build time keymap validation tool)
+
+cc_library {
+    name: "libinput",
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "Input.cpp",
+        "InputDevice.cpp",
+        "Keyboard.cpp",
+        "KeyCharacterMap.cpp",
+        "KeyLayoutMap.cpp",
+        "VirtualKeyMap.cpp",
+    ],
+
+    clang: true,
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+
+    target: {
+        android: {
+            srcs: [
+                "IInputFlinger.cpp",
+                "InputTransport.cpp",
+                "VelocityControl.cpp",
+                "VelocityTracker.cpp",
+            ],
+
+            shared_libs: [
+                "libutils",
+                "libbinder",
+            ],
+
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+        host: {
+            shared: {
+                enabled: false,
+            },
+        },
+    },
+}
+
+subdirs = ["tests"]
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
deleted file mode 100644
index 746de66..0000000
--- a/libs/input/Android.mk
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# libinput is partially built for the host (used by build time keymap validation tool)
-# These files are common to host and target builds.
-
-commonSources := \
-    Input.cpp \
-    InputDevice.cpp \
-    Keyboard.cpp \
-    KeyCharacterMap.cpp \
-    KeyLayoutMap.cpp \
-    VirtualKeyMap.cpp
-
-deviceSources := \
-    $(commonSources) \
-    IInputFlinger.cpp \
-    InputTransport.cpp \
-    VelocityControl.cpp \
-    VelocityTracker.cpp
-
-hostSources := \
-    $(commonSources)
-
-# For the host
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= $(hostSources)
-
-LOCAL_MODULE:= libinput
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= $(deviceSources)
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libcutils \
-	libutils \
-	libbinder
-
-LOCAL_MODULE:= libinput
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index e009731..003e73d 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -28,7 +28,7 @@
 
 class BpInputFlinger : public BpInterface<IInputFlinger> {
 public:
-    BpInputFlinger(const sp<IBinder>& impl) :
+    explicit BpInputFlinger(const sp<IBinder>& impl) :
             BpInterface<IInputFlinger>(impl) { }
 
     virtual status_t doSomething() {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index d755ed3..4287abe 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,6 +21,7 @@
 #include <ctype.h>
 
 #include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
 
 namespace android {
 
@@ -87,17 +88,23 @@
         const String8& name, InputDeviceConfigurationFileType type) {
     // Search system repository.
     String8 path;
-    path.setTo(getenv("ANDROID_ROOT"));
-    path.append("/usr/");
-    appendInputDeviceConfigurationFileRelativePath(path, name, type);
+
+    // Treblized input device config files will be located /odm/usr or /vendor/usr.
+    const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+    for (size_t i = 0; i < size(rootsForPartition); i++) {
+        path.setTo(rootsForPartition[i]);
+        path.append("/usr/");
+        appendInputDeviceConfigurationFileRelativePath(path, name, type);
 #if DEBUG_PROBE
-    ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+        ALOGD("Probing for system provided input device configuration file: path='%s'",
+              path.string());
 #endif
-    if (!access(path.string(), R_OK)) {
+        if (!access(path.string(), R_OK)) {
 #if DEBUG_PROBE
-        ALOGD("Found");
+            ALOGD("Found");
 #endif
-        return path;
+            return path;
+        }
     }
 
     // Search user repository.
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 2dff4e0..af1c0af 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -19,19 +19,18 @@
 // Log debug messages about touch event resampling
 #define DEBUG_RESAMPLING 0
 
-
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <math.h>
-#include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
-#include <input/InputTransport.h>
+#include <log/log.h>
 
+#include <input/InputTransport.h>
 
 namespace android {
 
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 9a01395..07f2289 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -208,7 +208,6 @@
 }
 
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
-    int32_t mask;
     switch (keyCode) {
     case AKEYCODE_ALT_LEFT:
         return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..029a420
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,28 @@
+// Build the unit tests.
+cc_test {
+    name: "libinput_tests",
+    test_per_src: true,
+    srcs: [
+        "InputChannel_test.cpp",
+        "InputEvent_test.cpp",
+        "InputPublisherAndConsumer_test.cpp",
+    ],
+    shared_libs: [
+        "libinput",
+        "libcutils",
+        "libutils",
+        "libbinder",
+        "libui",
+    ]
+}
+
+// NOTE: This is a compile time test, and does not need to be
+// run. All assertions are static_asserts and will fail during
+// buildtime if something's wrong.
+cc_library_static {
+    name: "StructLayout_test",
+    srcs: ["StructLayout_test.cpp"],
+    cflags: [
+        "-O0",
+    ],
+}
diff --git a/libs/input/tests/Android.mk b/libs/input/tests/Android.mk
deleted file mode 100644
index 5bfa3d4..0000000
--- a/libs/input/tests/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-
-# Build the unit tests.
-test_src_files := \
-    InputChannel_test.cpp \
-    InputEvent_test.cpp \
-    InputPublisherAndConsumer_test.cpp
-
-shared_libraries := \
-    libinput \
-    libcutils \
-    libutils \
-    libbinder \
-    libui \
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# NOTE: This is a compile time test, and does not need to be
-# run. All assertions are static_asserts and will fail during
-# buildtime if something's wrong.
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SRC_FILES := StructLayout_test.cpp
-LOCAL_MODULE := StructLayout_test
-LOCAL_CFLAGS := -std=c++11 -O0
-LOCAL_MULTILIB := both
-include $(BUILD_STATIC_LIBRARY)
-
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8d73f45..81b9953 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -20,7 +20,7 @@
 namespace android {
 
 #define CHECK_OFFSET(type, member, expected_offset) \
-  static_assert((offsetof(type, member) == expected_offset), "")
+  static_assert((offsetof(type, member) == (expected_offset)), "")
 
 struct Foo {
   uint32_t dummy;
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
new file mode 100644
index 0000000..3ef8b4a
--- /dev/null
+++ b/libs/math/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+    name: "libmath",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/math/MODULE_LICENSE_APACHE2 b/libs/math/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/math/MODULE_LICENSE_APACHE2
diff --git a/libs/math/NOTICE b/libs/math/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/math/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h
new file mode 100644
index 0000000..5cb725d
--- /dev/null
+++ b/libs/math/include/math/TMatHelpers.h
@@ -0,0 +1,644 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <math/quat.h>
+#include <math/TVecHelpers.h>
+
+#include  <utils/String8.h>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+#   define LIKELY( exp )    (__builtin_expect( !!(exp), true ))
+#   define UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
+#else
+#   define LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
+#   define UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/mat*.h
+ */
+
+
+/*
+ * Matrix utilities
+ */
+
+namespace matrix {
+
+inline constexpr int     transpose(int v)    { return v; }
+inline constexpr float   transpose(float v)  { return v; }
+inline constexpr double  transpose(double v) { return v; }
+
+inline constexpr int     trace(int v)    { return v; }
+inline constexpr float   trace(float v)  { return v; }
+inline constexpr double  trace(double v) { return v; }
+
+/*
+ * Matrix inversion
+ */
+template<typename MATRIX>
+MATRIX PURE gaussJordanInverse(const MATRIX& src) {
+    typedef typename MATRIX::value_type T;
+    static constexpr unsigned int N = MATRIX::NUM_ROWS;
+    MATRIX tmp(src);
+    MATRIX inverted(1);
+
+    for (size_t i = 0; i < N; ++i) {
+        // look for largest element in i'th column
+        size_t swap = i;
+        T t = std::abs(tmp[i][i]);
+        for (size_t j = i + 1; j < N; ++j) {
+            const T t2 = std::abs(tmp[j][i]);
+            if (t2 > t) {
+                swap = j;
+                t = t2;
+            }
+        }
+
+        if (swap != i) {
+            // swap columns.
+            std::swap(tmp[i], tmp[swap]);
+            std::swap(inverted[i], inverted[swap]);
+        }
+
+        const T denom(tmp[i][i]);
+        for (size_t k = 0; k < N; ++k) {
+            tmp[i][k] /= denom;
+            inverted[i][k] /= denom;
+        }
+
+        // Factor out the lower triangle
+        for (size_t j = 0; j < N; ++j) {
+            if (j != i) {
+                const T d = tmp[j][i];
+                for (size_t k = 0; k < N; ++k) {
+                    tmp[j][k] -= tmp[i][k] * d;
+                    inverted[j][k] -= inverted[i][k] * d;
+                }
+            }
+        }
+    }
+
+    return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// 2x2 matrix inverse is easy.
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
+    typedef typename MATRIX::value_type T;
+
+    // Assuming the input matrix is:
+    // | a b |
+    // | c d |
+    //
+    // The analytic inverse is
+    // | d -b |
+    // | -c a | / (a d - b c)
+    //
+    // Importantly, our matrices are column-major!
+
+    MATRIX inverted(MATRIX::NO_INIT);
+
+    const T a = x[0][0];
+    const T c = x[0][1];
+    const T b = x[1][0];
+    const T d = x[1][1];
+
+    const T det((a * d) - (b * c));
+    inverted[0][0] =  d / det;
+    inverted[0][1] = -c / det;
+    inverted[1][0] = -b / det;
+    inverted[1][1] =  a / det;
+    return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// From the Wikipedia article on matrix inversion's section on fast 3x3
+// matrix inversion:
+// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
+    typedef typename MATRIX::value_type T;
+
+    // Assuming the input matrix is:
+    // | a b c |
+    // | d e f |
+    // | g h i |
+    //
+    // The analytic inverse is
+    // | A B C |^T
+    // | D E F |
+    // | G H I | / determinant
+    //
+    // Which is
+    // | A D G |
+    // | B E H |
+    // | C F I | / determinant
+    //
+    // Where:
+    // A = (ei - fh), B = (fg - di), C = (dh - eg)
+    // D = (ch - bi), E = (ai - cg), F = (bg - ah)
+    // G = (bf - ce), H = (cd - af), I = (ae - bd)
+    //
+    // and the determinant is a*A + b*B + c*C (The rule of Sarrus)
+    //
+    // Importantly, our matrices are column-major!
+
+    MATRIX inverted(MATRIX::NO_INIT);
+
+    const T a = x[0][0];
+    const T b = x[1][0];
+    const T c = x[2][0];
+    const T d = x[0][1];
+    const T e = x[1][1];
+    const T f = x[2][1];
+    const T g = x[0][2];
+    const T h = x[1][2];
+    const T i = x[2][2];
+
+    // Do the full analytic inverse
+    const T A = e * i - f * h;
+    const T B = f * g - d * i;
+    const T C = d * h - e * g;
+    inverted[0][0] = A;                 // A
+    inverted[0][1] = B;                 // B
+    inverted[0][2] = C;                 // C
+    inverted[1][0] = c * h - b * i;     // D
+    inverted[1][1] = a * i - c * g;     // E
+    inverted[1][2] = b * g - a * h;     // F
+    inverted[2][0] = b * f - c * e;     // G
+    inverted[2][1] = c * d - a * f;     // H
+    inverted[2][2] = a * e - b * d;     // I
+
+    const T det(a * A + b * B + c * C);
+    for (size_t col = 0; col < 3; ++col) {
+        for (size_t row = 0; row < 3; ++row) {
+            inverted[col][row] /= det;
+        }
+    }
+
+    return inverted;
+}
+
+/**
+ * Inversion function which switches on the matrix size.
+ * @warning This function assumes the matrix is invertible. The result is
+ * undefined if it is not. It is the responsibility of the caller to
+ * make sure the matrix is not singular.
+ */
+template <typename MATRIX>
+inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
+    static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
+    return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
+          ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
+                    gaussJordanInverse<MATRIX>(matrix));
+}
+
+template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
+CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+    // pre-requisite:
+    //  lhs : D columns, R rows
+    //  rhs : C columns, D rows
+    //  res : C columns, R rows
+
+    static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
+            "matrices can't be multiplied. invalid dimensions.");
+    static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
+            "invalid dimension of matrix multiply result.");
+    static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
+            "invalid dimension of matrix multiply result.");
+
+    MATRIX_R res(MATRIX_R::NO_INIT);
+    for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
+        res[col] = lhs * rhs[col];
+    }
+    return res;
+}
+
+// transpose. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
+    // for now we only handle square matrix transpose
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
+    MATRIX result(MATRIX::NO_INIT);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
+            result[col][row] = transpose(m[row][col]);
+        }
+    }
+    return result;
+}
+
+// trace. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
+    typename MATRIX::value_type result(0);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        result += trace(m[col][col]);
+    }
+    return result;
+}
+
+// diag. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
+    typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        result[col] = m[col][col];
+    }
+    return result;
+}
+
+//------------------------------------------------------------------------------
+// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
+template <typename MATRIX>
+TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
+    typedef typename MATRIX::value_type T;
+
+    TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
+
+    // Compute the trace to see if it is positive or not.
+    const T trace = mat[0][0] + mat[1][1] + mat[2][2];
+
+    // check the sign of the trace
+    if (LIKELY(trace > 0)) {
+        // trace is positive
+        T s = std::sqrt(trace + 1);
+        quat.w = T(0.5) * s;
+        s = T(0.5) / s;
+        quat.x = (mat[1][2] - mat[2][1]) * s;
+        quat.y = (mat[2][0] - mat[0][2]) * s;
+        quat.z = (mat[0][1] - mat[1][0]) * s;
+    } else {
+        // trace is negative
+
+        // Find the index of the greatest diagonal
+        size_t i = 0;
+        if (mat[1][1] > mat[0][0]) { i = 1; }
+        if (mat[2][2] > mat[i][i]) { i = 2; }
+
+        // Get the next indices: (n+1)%3
+        static constexpr size_t next_ijk[3] = { 1, 2, 0 };
+        size_t j = next_ijk[i];
+        size_t k = next_ijk[j];
+        T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
+        quat[i] = T(0.5) * s;
+        if (s != 0) {
+            s = T(0.5) / s;
+        }
+        quat.w  = (mat[j][k] - mat[k][j]) * s;
+        quat[j] = (mat[i][j] + mat[j][i]) * s;
+        quat[k] = (mat[i][k] + mat[k][i]) * s;
+    }
+    return quat;
+}
+
+template <typename MATRIX>
+String8 asString(const MATRIX& m) {
+    String8 s;
+    for (size_t c = 0; c < MATRIX::col_size(); c++) {
+        s.append("|  ");
+        for (size_t r = 0; r < MATRIX::row_size(); r++) {
+            s.appendFormat("%7.2f  ", m[r][c]);
+        }
+        s.append("|\n");
+    }
+    return s;
+}
+
+}  // namespace matrix
+
+// -------------------------------------------------------------------------------------
+
+/*
+ * TMatProductOperators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class BASE, typename T>
+class TMatProductOperators {
+public:
+    // multiply by a scalar
+    BASE<T>& operator *= (T v) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            lhs[col] *= v;
+        }
+        return lhs;
+    }
+
+    //  matrix *= matrix
+    template<typename U>
+    const BASE<T>& operator *= (const BASE<U>& rhs) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        lhs = matrix::multiply<BASE<T> >(lhs, rhs);
+        return lhs;
+    }
+
+    // divide by a scalar
+    BASE<T>& operator /= (T v) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            lhs[col] /= v;
+        }
+        return lhs;
+    }
+
+    // matrix * matrix, result is a matrix of the same type than the lhs matrix
+    template<typename U>
+    friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+        return matrix::multiply<BASE<T> >(lhs, rhs);
+    }
+};
+
+/*
+ * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
+ *
+ * BASE only needs to implement:
+ *  - operator[]
+ *  - col_type
+ *  - row_type
+ *  - COL_SIZE
+ *  - ROW_SIZE
+ *
+ * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template<template<typename U> class BASE, typename T>
+class TMatSquareFunctions {
+public:
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
+        return matrix::inverse(matrix);
+    }
+    friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
+        return matrix::transpose(m);
+    }
+    friend inline constexpr T PURE trace(const BASE<T>& m) {
+        return matrix::trace(m);
+    }
+};
+
+template<template<typename U> class BASE, typename T>
+class TMatHelpers {
+public:
+    constexpr inline size_t getColumnSize() const   { return BASE<T>::COL_SIZE; }
+    constexpr inline size_t getRowSize() const      { return BASE<T>::ROW_SIZE; }
+    constexpr inline size_t getColumnCount() const  { return BASE<T>::NUM_COLS; }
+    constexpr inline size_t getRowCount() const     { return BASE<T>::NUM_ROWS; }
+    constexpr inline size_t size()  const           { return BASE<T>::ROW_SIZE; }  // for TVec*<>
+
+    // array access
+    constexpr T const* asArray() const {
+        return &static_cast<BASE<T> const &>(*this)[0][0];
+    }
+
+    // element access
+    inline constexpr T const& operator()(size_t row, size_t col) const {
+        return static_cast<BASE<T> const &>(*this)[col][row];
+    }
+
+    inline T& operator()(size_t row, size_t col) {
+        return static_cast<BASE<T>&>(*this)[col][row];
+    }
+
+    template <typename VEC>
+    static CONSTEXPR BASE<T> translate(const VEC& t) {
+        BASE<T> r;
+        r[BASE<T>::NUM_COLS-1] = t;
+        return r;
+    }
+
+    template <typename VEC>
+    static constexpr BASE<T> scale(const VEC& s) {
+        return BASE<T>(s);
+    }
+
+    friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            m[col] = abs(m[col]);
+        }
+        return m;
+    }
+};
+
+// functions for 3x3 and 4x4 matrices
+template<template<typename U> class BASE, typename T>
+class TMatTransform {
+public:
+    inline constexpr TMatTransform() {
+        static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
+    }
+
+    template <typename A, typename VEC>
+    static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
+        BASE<T> r;
+        T c = std::cos(radian);
+        T s = std::sin(radian);
+        if (about.x == 1 && about.y == 0 && about.z == 0) {
+            r[1][1] = c;   r[2][2] = c;
+            r[1][2] = s;   r[2][1] = -s;
+        } else if (about.x == 0 && about.y == 1 && about.z == 0) {
+            r[0][0] = c;   r[2][2] = c;
+            r[2][0] = s;   r[0][2] = -s;
+        } else if (about.x == 0 && about.y == 0 && about.z == 1) {
+            r[0][0] = c;   r[1][1] = c;
+            r[0][1] = s;   r[1][0] = -s;
+        } else {
+            VEC nabout = normalize(about);
+            typename VEC::value_type x = nabout.x;
+            typename VEC::value_type y = nabout.y;
+            typename VEC::value_type z = nabout.z;
+            T nc = 1 - c;
+            T xy = x * y;
+            T yz = y * z;
+            T zx = z * x;
+            T xs = x * s;
+            T ys = y * s;
+            T zs = z * s;
+            r[0][0] = x*x*nc +  c;    r[1][0] =  xy*nc - zs;    r[2][0] =  zx*nc + ys;
+            r[0][1] =  xy*nc + zs;    r[1][1] = y*y*nc +  c;    r[2][1] =  yz*nc - xs;
+            r[0][2] =  zx*nc - ys;    r[1][2] =  yz*nc + xs;    r[2][2] = z*z*nc +  c;
+
+            // Clamp results to -1, 1.
+            for (size_t col = 0; col < 3; ++col) {
+                for (size_t row = 0; row < 3; ++row) {
+                    r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+                }
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Create a matrix from euler angles using YPR around YXZ respectively
+     * @param yaw about Y axis
+     * @param pitch about X axis
+     * @param roll about Z axis
+     */
+    template <
+        typename Y, typename P, typename R,
+        typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+        typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+        typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+    >
+    static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
+        return eulerZYX(roll, pitch, yaw);
+    }
+
+    /**
+     * Create a matrix from euler angles using YPR around ZYX respectively
+     * @param roll about X axis
+     * @param pitch about Y axis
+     * @param yaw about Z axis
+     *
+     * The euler angles are applied in ZYX order. i.e: a vector is first rotated
+     * about X (roll) then Y (pitch) and then Z (yaw).
+     */
+    template <
+    typename Y, typename P, typename R,
+    typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+    typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+    typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+    >
+    static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
+        BASE<T> r;
+        T cy = std::cos(yaw);
+        T sy = std::sin(yaw);
+        T cp = std::cos(pitch);
+        T sp = std::sin(pitch);
+        T cr = std::cos(roll);
+        T sr = std::sin(roll);
+        T cc = cr * cy;
+        T cs = cr * sy;
+        T sc = sr * cy;
+        T ss = sr * sy;
+        r[0][0] = cp * cy;
+        r[0][1] = cp * sy;
+        r[0][2] = -sp;
+        r[1][0] = sp * sc - cs;
+        r[1][1] = sp * ss + cc;
+        r[1][2] = cp * sr;
+        r[2][0] = sp * cc + ss;
+        r[2][1] = sp * cs - sc;
+        r[2][2] = cp * cr;
+
+        // Clamp results to -1, 1.
+        for (size_t col = 0; col < 3; ++col) {
+            for (size_t row = 0; row < 3; ++row) {
+                r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+            }
+        }
+        return r;
+    }
+
+    TQuaternion<T> toQuaternion() const {
+        return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
+    }
+};
+
+
+template <template<typename T> class BASE, typename T>
+class TMatDebug {
+public:
+    friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
+        for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
+            if (row != 0) {
+                stream << std::endl;
+            }
+            if (row == 0) {
+                stream << "/ ";
+            } else if (row == BASE<T>::NUM_ROWS-1) {
+                stream << "\\ ";
+            } else {
+                stream << "| ";
+            }
+            for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+                stream << std::setw(10) << std::to_string(m[col][row]);
+            }
+            if (row == 0) {
+                stream << " \\";
+            } else if (row == BASE<T>::NUM_ROWS-1) {
+                stream << " /";
+            } else {
+                stream << " |";
+            }
+        }
+        return stream;
+    }
+
+    String8 asString() const {
+        return matrix::asString(static_cast<const BASE<T>&>(*this));
+    }
+};
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif //LIKELY_DEFINED_LOCAL
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h
new file mode 100644
index 0000000..f0a71ae
--- /dev/null
+++ b/libs/math/include/math/TQuatHelpers.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <math/vec3.h>
+
+#define PURE __attribute__((pure))
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/quat.h
+ */
+
+
+/*
+ * TQuatProductOperators implements basic arithmetic and basic compound assignment
+ * operators on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class QUATERNION, typename T>
+class TQuatProductOperators {
+public:
+    /* compound assignment from a another quaternion of the same size but different
+     * element type.
+     */
+    template <typename OTHER>
+    QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) {
+        QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
+        q = q * r;
+        return q;
+    }
+
+    /* compound assignment products by a scalar
+     */
+    QUATERNION<T>& operator *= (T v) {
+        QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+        for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+            lhs[i] *= v;
+        }
+        return lhs;
+    }
+    QUATERNION<T>& operator /= (T v) {
+        QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+        for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+            lhs[i] /= v;
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between quaternion of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
+        // could be written as:
+        //  return QUATERNION<T>(
+        //            q.w*r.w - dot(q.xyz, r.xyz),
+        //            q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
+
+        return QUATERNION<T>(
+                q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+                q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+                q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+                q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) {
+        // note: if q is known to be a unit quaternion, then this simplifies to:
+        //  TVec3<T> t = 2 * cross(q.xyz, v)
+        //  return v + (q.w * t) + cross(q.xyz, t)
+        return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
+    }
+
+
+    /* For quaternions, we use explicit "by a scalar" products because it's much faster
+     * than going (implicitly) through the quaternion multiplication.
+     * For reference: we could use the code below instead, but it would be a lot slower.
+     *  friend inline
+     *  constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) {
+     *      return BASE<T>(
+     *              q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+     *              q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+     *              q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+     *              q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+     *
+     */
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) {
+        // don't pass q by reference because we need a copy anyways
+        return q *= scalar;
+    }
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) {
+        // don't pass q by reference because we need a copy anyways
+        return q *= scalar;
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) {
+        // don't pass q by reference because we need a copy anyways
+        return q /= scalar;
+    }
+};
+
+
+/*
+ * TQuatFunctions implements functions on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatFunctions {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    template<typename RT>
+    friend inline
+    constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
+        return p.x * q.x +
+               p.y * q.y +
+               p.z * q.z +
+               p.w * q.w;
+    }
+
+    friend inline
+    constexpr T PURE norm(const QUATERNION<T>& q) {
+        return std::sqrt( dot(q, q) );
+    }
+
+    friend inline
+    constexpr T PURE length(const QUATERNION<T>& q) {
+        return norm(q);
+    }
+
+    friend inline
+    constexpr T PURE length2(const QUATERNION<T>& q) {
+        return dot(q, q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) {
+        return length(q) ? q / length(q) : QUATERNION<T>(1);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) {
+        return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) {
+        return conj(q) * (1 / dot(q, q));
+    }
+
+    friend inline
+    constexpr T PURE real(const QUATERNION<T>& q) {
+        return q.w;
+    }
+
+    friend inline
+    constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) {
+        return q.xyz;
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) {
+        return QUATERNION<T>(q.xyz, 0);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
+        return unreal(p*q);
+    }
+
+    friend inline
+    QUATERNION<T> PURE exp(const QUATERNION<T>& q) {
+        const T nq(norm(q.xyz));
+        return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq));
+    }
+
+    friend inline
+    QUATERNION<T> PURE log(const QUATERNION<T>& q) {
+        const T nq(norm(q));
+        return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq));
+    }
+
+    friend inline
+    QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) {
+        // could also be computed as: exp(a*log(q));
+        const T nq(norm(q));
+        const T theta(a*std::acos(q.w / nq));
+        return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
+    }
+
+    friend inline
+    QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        // could also be computed as: pow(q * inverse(p), t) * p;
+        const T d = dot(p, q);
+        const T npq = sqrt(dot(p, p) * dot(q, q));  // ||p|| * ||q||
+        const T a = std::acos(std::abs(d) / npq);
+        const T a0 = a * (1 - t);
+        const T a1 = a * t;
+        const T isina = 1 / sin(a);
+        const T s0 = std::sin(a0) * isina;
+        const T s1 = std::sin(a1) * isina;
+        // ensure we're taking the "short" side
+        return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        return ((1 - t) * p) + (t * q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        return normalize(lerp(p, q, t));
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) {
+        return q.w < 0 ? -q : q;
+    }
+};
+
+/*
+ * TQuatDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatDebug {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) {
+        return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >";
+    }
+};
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
new file mode 100644
index 0000000..20f852f
--- /dev/null
+++ b/libs/math/include/math/TVecHelpers.h
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <limits>
+#include <iostream>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/vec{2|3|4}.h
+ */
+
+/*
+ * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class VECTOR, typename T>
+class TVecAddOperators {
+public:
+    /* compound assignment from a another vector of the same size but different
+     * element type.
+     */
+    template<typename OTHER>
+    VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] += v[i];
+        }
+        return lhs;
+    }
+    template<typename OTHER>
+    VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] -= v[i];
+        }
+        return lhs;
+    }
+
+    /* compound assignment from a another vector of the same type.
+     * These operators can be used for implicit conversion and  handle operations
+     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+     * to a vector (assuming the BASE<T> allows it).
+     */
+    VECTOR<T>& operator +=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] += v[i];
+        }
+        return lhs;
+    }
+    VECTOR<T>& operator -=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] -= v[i];
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between vectors of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv += rv;
+    }
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv -= rv;
+    }
+
+    /* The operators below (which are not templates once this class is instanced,
+     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+     * These handle operations like "vector + scalar" and "scalar + vector" by
+     * letting the compiler implicitly convert a scalar to a vector (assuming
+     * the BASE<T> allows it).
+     */
+    friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv += rv;
+    }
+    friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv -= rv;
+    }
+};
+
+template<template<typename T> class VECTOR, typename T>
+class TVecProductOperators {
+public:
+    /* compound assignment from a another vector of the same size but different
+     * element type.
+     */
+    template<typename OTHER>
+    VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] *= v[i];
+        }
+        return lhs;
+    }
+    template<typename OTHER>
+    VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] /= v[i];
+        }
+        return lhs;
+    }
+
+    /* compound assignment from a another vector of the same type.
+     * These operators can be used for implicit conversion and  handle operations
+     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+     * to a vector (assuming the BASE<T> allows it).
+     */
+    VECTOR<T>& operator *=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] *= v[i];
+        }
+        return lhs;
+    }
+    VECTOR<T>& operator /=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] /= v[i];
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between vectors of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv *= rv;
+    }
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv /= rv;
+    }
+
+    /* The operators below (which are not templates once this class is instanced,
+     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+     * These handle operations like "vector * scalar" and "scalar * vector" by
+     * letting the compiler implicitly convert a scalar to a vector (assuming
+     * the BASE<T> allows it).
+     */
+    friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv *= rv;
+    }
+    friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv /= rv;
+    }
+};
+
+/*
+ * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ *
+ * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecUnaryOperators {
+public:
+    VECTOR<T>& operator ++() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.size(); i++) {
+            ++rhs[i];
+        }
+        return rhs;
+    }
+
+    VECTOR<T>& operator --() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.size(); i++) {
+            --rhs[i];
+        }
+        return rhs;
+    }
+
+    CONSTEXPR VECTOR<T> operator -() const {
+        VECTOR<T> r(VECTOR<T>::NO_INIT);
+        VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+        for (size_t i = 0; i < r.size(); i++) {
+            r[i] = -rv[i];
+        }
+        return r;
+    }
+};
+
+/*
+ * TVecComparisonOperators implements relational/comparison operators
+ * on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecComparisonOperators {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    template<typename RT>
+    friend inline
+    bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++)
+            if (lv[i] != rv[i])
+                return false;
+        return true;
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !operator ==(lv, rv);
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++) {
+            if (lv[i] == rv[i]) {
+                continue;
+            }
+            return lv[i] > rv[i];
+        }
+        return false;
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !(lv > rv);
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++) {
+            if (lv[i] == rv[i]) {
+                continue;
+            }
+            return lv[i] < rv[i];
+        }
+        return false;
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !(lv < rv);
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] == rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] != rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] < rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] <= rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] > rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] >= rv[i];
+        }
+        return r;
+    }
+};
+
+/*
+ * TVecFunctions implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecFunctions {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    template<typename RT>
+    friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        T r(0);
+        for (size_t i = 0; i < lv.size(); i++) {
+            //r = std::fma(lv[i], rv[i], r);
+            r += lv[i] * rv[i];
+        }
+        return r;
+    }
+
+    friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
+        return std::sqrt(dot(lv, lv));
+    }
+
+    friend inline constexpr T PURE length(const VECTOR<T>& lv) {
+        return norm(lv);
+    }
+
+    friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
+        return dot(lv, lv);
+    }
+
+    friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
+        return norm2(lv);
+    }
+
+    template<typename RT>
+    friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return length(rv - lv);
+    }
+
+    template<typename RT>
+    friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return length2(rv - lv);
+    }
+
+    friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
+        return lv * (T(1) / length(lv));
+    }
+
+    friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
+        return T(1) / v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::abs(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::floor(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::ceil(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::round(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = T(1) / std::sqrt(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::sqrt(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::pow(v[i], p);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
+        return clamp(lv, T(0), T(1));
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
+        for (size_t i = 0; i< v.size(); i++) {
+            v[i] = std::min(max, std::max(min, v[i]));
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
+        for (size_t i = 0; i<lv.size(); i++) {
+            //a[i] = std::fma(lv[i], rv[i], a[i]);
+            a[i] += (lv[i] * rv[i]);
+        }
+        return a;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::min(u[i], v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::max(u[i], v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
+        T r(std::numeric_limits<T>::lowest());
+        for (size_t i = 0; i < v.size(); i++) {
+            r = std::max(r, v[i]);
+        }
+        return r;
+    }
+
+    friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
+        T r(std::numeric_limits<T>::max());
+        for (size_t i = 0; i < v.size(); i++) {
+            r = std::min(r, v[i]);
+        }
+        return r;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = f(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            if (v[i] != T(0)) return true;
+        }
+        return false;
+    }
+
+    friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) {
+        bool result = true;
+        for (size_t i = 0; i < v.size(); i++) {
+            result &= (v[i] != T(0));
+        }
+        return result;
+    }
+
+    template<typename R>
+    friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) {
+        VECTOR<R> result;
+        for (size_t i = 0; i < v.size(); i++) {
+            result[i] = f(v[i]);
+        }
+        return result;
+    }
+};
+
+/*
+ * TVecDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecDebug {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
+        stream << "< ";
+        for (size_t i = 0; i < v.size() - 1; i++) {
+            stream << T(v[i]) << ", ";
+        }
+        stream << T(v[v.size() - 1]) << " >";
+        return stream;
+    }
+};
+
+#undef CONSTEXPR
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
new file mode 100644
index 0000000..615b840
--- /dev/null
+++ b/libs/math/include/math/half.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <iosfwd>
+#include <limits>
+#include <type_traits>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+#   define LIKELY( exp )    (__builtin_expect( !!(exp), true ))
+#   define UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
+#else
+#   define LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
+#   define UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+
+/*
+ * half-float
+ *
+ *  1   5       10
+ * +-+------+------------+
+ * |s|eee.ee|mm.mmmm.mmmm|
+ * +-+------+------------+
+ *
+ * minimum (denormal) value: 2^-24 = 5.96e-8
+ * minimum (normal) value:   2^-14 = 6.10e-5
+ * maximum value:            2-2^-10 = 65504
+ *
+ * Integers between 0 and 2048 can be represented exactly
+ */
+class half {
+    struct fp16 {
+        uint16_t bits = 0;
+        fp16() noexcept = default;
+        explicit constexpr fp16(uint16_t b) noexcept : bits(b) { }
+        void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); }
+        void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); }
+        void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); }
+        constexpr unsigned int getS() const noexcept { return  bits >> 15u; }
+        constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
+        constexpr unsigned int getM() const noexcept { return  bits         & 0x3FFu; }
+    };
+    struct fp32 {
+        union {
+            uint32_t bits = 0;
+            float fp;
+        };
+        fp32() noexcept = default;
+        explicit constexpr fp32(float f) : fp(f) { }
+        void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); }
+        void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); }
+        void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); }
+        constexpr unsigned int getS() const noexcept { return  bits >> 31u; }
+        constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
+        constexpr unsigned int getM() const noexcept { return  bits         & 0x7FFFFFu; }
+    };
+
+public:
+    CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
+    CONSTEXPR operator float() const noexcept { return htof(mBits); }
+
+    uint16_t getBits() const noexcept { return mBits.bits; }
+    unsigned int getExponent() const noexcept { return mBits.getE(); }
+    unsigned int getMantissa() const noexcept { return mBits.getM(); }
+
+private:
+    friend class std::numeric_limits<half>;
+    friend CONSTEXPR half operator"" _hf(long double v);
+
+    enum Binary { binary };
+    explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
+    static CONSTEXPR fp16 ftoh(float v) noexcept;
+    static CONSTEXPR float htof(fp16 v) noexcept;
+    fp16 mBits;
+};
+
+inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept {
+    fp16 out;
+    fp32 in(v);
+    if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan
+        out.setE(0x1F);
+        out.setM(in.getM() ? 0x200 : 0);
+    } else {
+        int e = static_cast<int>(in.getE()) - 127 + 15;
+        if (e >= 0x1F) {
+            // overflow
+            out.setE(0x31); // +/- inf
+        } else if (e <= 0) {
+            // underflow
+            // flush to +/- 0
+        } else {
+            unsigned int m = in.getM();
+            out.setE(uint16_t(e));
+            out.setM(m >> 13);
+            if (m & 0x1000) {
+                // rounding
+                out.bits++;
+            }
+        }
+    }
+    out.setS(in.getS());
+    return out;
+}
+
+inline CONSTEXPR float half::htof(half::fp16 in) noexcept {
+    fp32 out;
+    if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan
+        out.setE(0xFF);
+        out.setM(in.getM() ? 0x400000 : 0);
+    } else {
+        if (in.getE() == 0) {
+            if (in.getM()) {
+                // TODO: denormal half float, treat as zero for now
+                // (it's stupid because they can be represented as regular float)
+            }
+        } else {
+            int e = static_cast<int>(in.getE()) - 15 + 127;
+            unsigned int m = in.getM();
+            out.setE(uint32_t(e));
+            out.setM(m << 13);
+        }
+    }
+    out.setS(in.getS());
+    return out.fp;
+}
+
+inline CONSTEXPR android::half operator"" _hf(long double v) {
+    return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits);
+}
+
+} // namespace android
+
+namespace std {
+
+template<> struct is_floating_point<android::half> : public std::true_type {};
+
+template<>
+class numeric_limits<android::half> {
+public:
+    typedef android::half type;
+
+    static constexpr const bool is_specialized = true;
+    static constexpr const bool is_signed = true;
+    static constexpr const bool is_integer = false;
+    static constexpr const bool is_exact = false;
+    static constexpr const bool has_infinity = true;
+    static constexpr const bool has_quiet_NaN = true;
+    static constexpr const bool has_signaling_NaN = false;
+    static constexpr const float_denorm_style has_denorm = denorm_absent;
+    static constexpr const bool has_denorm_loss = true;
+    static constexpr const bool is_iec559 = false;
+    static constexpr const bool is_bounded = true;
+    static constexpr const bool is_modulo = false;
+    static constexpr const bool traps = false;
+    static constexpr const bool tinyness_before = false;
+    static constexpr const float_round_style round_style = round_indeterminate;
+
+    static constexpr const int digits = 11;
+    static constexpr const int digits10 = 3;
+    static constexpr const int max_digits10 = 5;
+    static constexpr const int radix = 2;
+    static constexpr const int min_exponent = -13;
+    static constexpr const int min_exponent10 = -4;
+    static constexpr const int max_exponent = 16;
+    static constexpr const int max_exponent10 = 4;
+
+    inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); }
+    inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); }
+    inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); }
+    inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); }
+    inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); }
+    inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); }
+    inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); }
+    inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); }
+    inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
+};
+
+} // namespace std
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif // LIKELY_DEFINED_LOCAL
+
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
new file mode 100644
index 0000000..3e6cd4c
--- /dev/null
+++ b/libs/math/include/math/mat2.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/TMatHelpers.h>
+#include <math/vec2.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+/**
+ * A 2x2 column-major matrix class.
+ *
+ * Conceptually a 2x2 matrix is a an array of 2 column vec2:
+ *
+ * mat2 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m[0] & m[1] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m[0][0] & m[1][0] \\
+ *      m[0][1] & m[1][1] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m(0,0) & m(0,1) \\
+ *      m(1,0) & m(1,1) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
+ *
+ */
+template <typename T>
+class TMat22 :  public TVecUnaryOperators<TMat22, T>,
+                public TVecComparisonOperators<TMat22, T>,
+                public TVecAddOperators<TMat22, T>,
+                public TMatProductOperators<TMat22, T>,
+                public TMatSquareFunctions<TMat22, T>,
+                public TMatHelpers<TMat22, T>,
+                public TMatDebug<TMat22, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec2<T> col_type;
+    typedef TVec2<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat22(const TMat22&) = default;
+    ~TMat22() = default;
+    TMat22& operator = (const TMat22&) = default;
+
+    /**
+     *  constructors
+     */
+
+    /**
+     * leaves object uninitialized. use with caution.
+     */
+    explicit constexpr TMat22(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+
+    /**
+     * initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      1 & 0 \\
+     *      0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat22();
+
+    /**
+     * initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v & 0 \\
+     *      0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat22(U v);
+
+    /**
+     * sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v[0] & 0 \\
+     *      0 & v[1] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(const TVec2<U>& v);
+
+    /**
+     * construct from another matrix of the same size
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(const TMat22<U>& rhs);
+
+    /**
+     * construct from 2 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v0 & v1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B>
+    CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1);
+
+    /** construct from 4 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      m[0][0] & m[1][0] \\
+     *      m[0][1] & m[1][1] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B,
+        typename C, typename D>
+    CONSTEXPR TMat22(A m00, B m01, C m10, D m11);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(U const* rawArray);
+
+    /**
+     * Rotate by radians in the 2D plane
+     */
+    static CONSTEXPR TMat22<T> rotate(T radian) {
+        TMat22<T> r(TMat22<T>::NO_INIT);
+        T c = std::cos(radian);
+        T s = std::sin(radian);
+        r[0][0] = c;   r[1][1] = c;
+        r[0][1] = s;   r[1][0] = -s;
+        return r;
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat22<T>::TMat22() {
+    m_value[0] = col_type(1, 0);
+    m_value[1] = col_type(0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U v) {
+    m_value[0] = col_type(v, 0);
+    m_value[1] = col_type(0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) {
+    m_value[0] = col_type(v.x, 0);
+    m_value[1] = col_type(0, v.y);
+}
+
+// construct from 4 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+    typename A, typename B,
+    typename C, typename D>
+CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) {
+    m_value[0] = col_type(m00, m01);
+    m_value[1] = col_type(m10, m11);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 2 column vectors.
+template <typename T>
+template <typename A, typename B>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) {
+    m_value[0] = v0;
+    m_value[1] = v1;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat22<U>::col_type result;
+    for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) {
+    typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(TMat22<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(U lhs, const TMat22<T>& rhs) {
+    return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) {
+    return matrix::diag(m);
+}
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat22<double> mat2d;
+typedef details::TMat22<float> mat2;
+typedef details::TMat22<float> mat2f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
new file mode 100644
index 0000000..5c8a9b2
--- /dev/null
+++ b/libs/math/include/math/mat3.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 3x3 column-major matrix class.
+ *
+ * Conceptually a 3x3 matrix is a an array of 3 column vec3:
+ *
+ * mat3 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m[0] & m[1] & m[2] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m[0][0] & m[1][0] & m[2][0] \\
+ *      m[0][1] & m[1][1] & m[2][1] \\
+ *      m[0][2] & m[1][2] & m[2][2] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m(0,0) & m(0,1) & m(0,2) \\
+ *      m(1,0) & m(1,1) & m(1,2) \\
+ *      m(2,0) & m(2,1) & m(2,2) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
+ *
+ */
+template <typename T>
+class TMat33 :  public TVecUnaryOperators<TMat33, T>,
+                public TVecComparisonOperators<TMat33, T>,
+                public TVecAddOperators<TMat33, T>,
+                public TMatProductOperators<TMat33, T>,
+                public TMatSquareFunctions<TMat33, T>,
+                public TMatTransform<TMat33, T>,
+                public TMatHelpers<TMat33, T>,
+                public TMatDebug<TMat33, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec3<T> col_type;
+    typedef TVec3<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat33(const TMat33&) = default;
+    ~TMat33() = default;
+    TMat33& operator = (const TMat33&) = default;
+
+    /**
+     *  constructors
+     */
+
+    /**
+     * leaves object uninitialized. use with caution.
+     */
+    explicit constexpr TMat33(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+
+    /**
+     * initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      1 & 0 & 0 \\
+     *      0 & 1 & 0 \\
+     *      0 & 0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat33();
+
+    /**
+     * initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v & 0 & 0 \\
+     *      0 & v & 0 \\
+     *      0 & 0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat33(U v);
+
+    /**
+     * sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v[0] & 0 & 0 \\
+     *      0 & v[1] & 0 \\
+     *      0 & 0 & v[2] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TVec3<U>& v);
+
+    /**
+     * construct from another matrix of the same size
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TMat33<U>& rhs);
+
+    /**
+     * construct from 3 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v0 & v1 & v2 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B, typename C>
+    CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2);
+
+    /** construct from 9 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      m[0][0] & m[1][0] & m[2][0] \\
+     *      m[0][1] & m[1][1] & m[2][1] \\
+     *      m[0][2] & m[1][2] & m[2][2] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B, typename C,
+        typename D, typename E, typename F,
+        typename G, typename H, typename I>
+    CONSTEXPR TMat33(
+           A m00, B m01, C m02,
+           D m10, E m11, F m12,
+           G m20, H m21, I m22);
+
+    /**
+     * construct from a quaternion
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TQuaternion<U>& q);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(U const* rawArray);
+
+    /**
+     * orthogonalize only works on matrices of size 3x3
+     */
+    friend inline
+    CONSTEXPR TMat33 orthogonalize(const TMat33& m) {
+        TMat33 ret(TMat33::NO_INIT);
+        ret[0] = normalize(m[0]);
+        ret[2] = normalize(cross(ret[0], m[1]));
+        ret[1] = normalize(cross(ret[2], ret[0]));
+        return ret;
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat33<T>::TMat33() {
+    m_value[0] = col_type(1, 0, 0);
+    m_value[1] = col_type(0, 1, 0);
+    m_value[2] = col_type(0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U v) {
+    m_value[0] = col_type(v, 0, 0);
+    m_value[1] = col_type(0, v, 0);
+    m_value[2] = col_type(0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) {
+    m_value[0] = col_type(v.x, 0, 0);
+    m_value[1] = col_type(0, v.y, 0);
+    m_value[2] = col_type(0, 0, v.z);
+}
+
+// construct from 9 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+    typename A, typename B, typename C,
+    typename D, typename E, typename F,
+    typename G, typename H, typename I>
+CONSTEXPR TMat33<T>::TMat33(
+        A m00, B m01, C m02,
+        D m10, E m11, F m12,
+        G m20, H m21, I m22) {
+    m_value[0] = col_type(m00, m01, m02);
+    m_value[1] = col_type(m10, m11, m12);
+    m_value[2] = col_type(m20, m21, m22);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 3 column vectors.
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) {
+    m_value[0] = v0;
+    m_value[1] = v1;
+    m_value[2] = v2;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) {
+    const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+    const U s = n > 0 ? 2/n : 0;
+    const U x = s*q.x;
+    const U y = s*q.y;
+    const U z = s*q.z;
+    const U xx = x*q.x;
+    const U xy = x*q.y;
+    const U xz = x*q.z;
+    const U xw = x*q.w;
+    const U yy = y*q.y;
+    const U yz = y*q.z;
+    const U yw = y*q.w;
+    const U zz = z*q.z;
+    const U zw = z*q.w;
+    m_value[0] = col_type(1-yy-zz,    xy+zw,    xz-yw);  // NOLINT
+    m_value[1] = col_type(  xy-zw,  1-xx-zz,    yz+xw);  // NOLINT
+    m_value[2] = col_type(  xz+yw,    yz-xw,  1-xx-yy);  // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat33<U>::col_type result;
+    for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) {
+    typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(TMat33<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(U lhs, const TMat33<T>& rhs) {
+    return rhs * lhs;
+}
+
+//------------------------------------------------------------------------------
+template <typename T>
+CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) {
+    TMat33<T> ret(TMat33<T>::NO_INIT);
+    ret[0] = normalize(m[0]);
+    ret[2] = normalize(cross(ret[0], m[1]));
+    ret[1] = normalize(cross(ret[2], ret[0]));
+    return ret;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) {
+    return matrix::diag(m);
+}
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat33<double> mat3d;
+typedef details::TMat33<float> mat3;
+typedef details::TMat33<float> mat3f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
new file mode 100644
index 0000000..6119ba7
--- /dev/null
+++ b/libs/math/include/math/mat4.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat3.h>
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 4x4 column-major matrix class.
+ *
+ * Conceptually a 4x4 matrix is a an array of 4 column double4:
+ *
+ * mat4 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m[0] & m[1] & m[2] & m[3] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ *      m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ *      m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ *      m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
+ *      m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
+ *      m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
+ *      m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
+ *
+ */
+template <typename T>
+class TMat44 :  public TVecUnaryOperators<TMat44, T>,
+                public TVecComparisonOperators<TMat44, T>,
+                public TVecAddOperators<TMat44, T>,
+                public TMatProductOperators<TMat44, T>,
+                public TMatSquareFunctions<TMat44, T>,
+                public TMatTransform<TMat44, T>,
+                public TMatHelpers<TMat44, T>,
+                public TMatDebug<TMat44, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec4<T> col_type;
+    typedef TVec4<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat44(const TMat44&) = default;
+    ~TMat44() = default;
+    TMat44& operator = (const TMat44&) = default;
+
+    /*
+     *  constructors
+     */
+
+    // leaves object uninitialized. use with caution.
+    explicit constexpr TMat44(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+    /** initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      1 & 0 & 0 & 0 \\
+     *      0 & 1 & 0 & 0 \\
+     *      0 & 0 & 1 & 0 \\
+     *      0 & 0 & 0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat44();
+
+    /** initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v & 0 & 0 & 0 \\
+     *      0 & v & 0 & 0 \\
+     *      0 & 0 & v & 0 \\
+     *      0 & 0 & 0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat44(U v);
+
+    /** sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v[0] & 0 & 0 & 0 \\
+     *      0 & v[1] & 0 & 0 \\
+     *      0 & 0 & v[2] & 0 \\
+     *      0 & 0 & 0 & v[3] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TVec4<U>& v);
+
+    // construct from another matrix of the same size
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
+
+    /** construct from 4 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v0 & v1 & v2 & v3 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B, typename C, typename D>
+    CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
+
+    /** construct from 16 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+     *      m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+     *      m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+     *      m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B, typename C, typename D,
+        typename E, typename F, typename G, typename H,
+        typename I, typename J, typename K, typename L,
+        typename M, typename N, typename O, typename P>
+    CONSTEXPR TMat44(
+            A m00, B m01, C m02, D m03,
+            E m10, F m11, G m12, H m13,
+            I m20, J m21, K m22, L m23,
+            M m30, N m31, O m32, P m33);
+
+    /**
+     * construct from a quaternion
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TQuaternion<U>& q);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(U const* rawArray);
+
+    /**
+     * construct from a 3x3 matrix
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TMat33<U>& matrix);
+
+    /**
+     * construct from a 3x3 matrix and 3d translation
+     */
+    template <typename U, typename V>
+    CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation);
+
+    /**
+     * construct from a 3x3 matrix and 4d last column.
+     */
+    template <typename U, typename V>
+    CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3);
+
+    /*
+     *  helpers
+     */
+
+    static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far);
+
+    static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far);
+
+    enum class Fov {
+        HORIZONTAL,
+        VERTICAL
+    };
+    static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL);
+
+    template <typename A, typename B, typename C>
+    static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
+
+    template <typename A>
+    static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) {
+        TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
+        return r.xyz / r.w;
+    }
+
+    template <typename A>
+    static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+        vertice = projectionMatrix * vertice;
+        return { vertice.xyz / vertice.w, 1 };
+    }
+
+    /**
+     * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
+     */
+    inline constexpr TMat33<T> upperLeft() const {
+        return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz);
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat44<T>::TMat44() {
+    m_value[0] = col_type(1, 0, 0, 0);
+    m_value[1] = col_type(0, 1, 0, 0);
+    m_value[2] = col_type(0, 0, 1, 0);
+    m_value[3] = col_type(0, 0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U v) {
+    m_value[0] = col_type(v, 0, 0, 0);
+    m_value[1] = col_type(0, v, 0, 0);
+    m_value[2] = col_type(0, 0, v, 0);
+    m_value[3] = col_type(0, 0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) {
+    m_value[0] = col_type(v.x, 0, 0, 0);
+    m_value[1] = col_type(0, v.y, 0, 0);
+    m_value[2] = col_type(0, 0, v.z, 0);
+    m_value[3] = col_type(0, 0, 0, v.w);
+}
+
+// construct from 16 scalars
+template<typename T>
+template <
+    typename A, typename B, typename C, typename D,
+    typename E, typename F, typename G, typename H,
+    typename I, typename J, typename K, typename L,
+    typename M, typename N, typename O, typename P>
+CONSTEXPR TMat44<T>::TMat44(
+        A m00, B m01, C m02, D m03,
+        E m10, F m11, G m12, H m13,
+        I m20, J m21, K m22, L m23,
+        M m30, N m31, O m32, P m33) {
+    m_value[0] = col_type(m00, m01, m02, m03);
+    m_value[1] = col_type(m10, m11, m12, m13);
+    m_value[2] = col_type(m20, m21, m22, m23);
+    m_value[3] = col_type(m30, m31, m32, m33);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 4 column vectors.
+template <typename T>
+template <typename A, typename B, typename C, typename D>
+CONSTEXPR TMat44<T>::TMat44(
+        const TVec4<A>& v0, const TVec4<B>& v1,
+        const TVec4<C>& v2, const TVec4<D>& v3) {
+    m_value[0] = col_type(v0);
+    m_value[1] = col_type(v1);
+    m_value[2] = col_type(v2);
+    m_value[3] = col_type(v3);
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) {
+    const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+    const U s = n > 0 ? 2/n : 0;
+    const U x = s*q.x;
+    const U y = s*q.y;
+    const U z = s*q.z;
+    const U xx = x*q.x;
+    const U xy = x*q.y;
+    const U xz = x*q.z;
+    const U xw = x*q.w;
+    const U yy = y*q.y;
+    const U yz = y*q.z;
+    const U yw = y*q.w;
+    const U zz = z*q.z;
+    const U zw = z*q.w;
+    m_value[0] = col_type(1-yy-zz,    xy+zw,    xz-yw,   0);
+    m_value[1] = col_type(  xy-zw,  1-xx-zz,    yz+xw,   0);  // NOLINT
+    m_value[2] = col_type(  xz+yw,    yz-xw,  1-xx-yy,   0);  // NOLINT
+    m_value[3] = col_type(      0,        0,        0,   1);  // NOLINT
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+    m_value[3] = col_type(      0,       0,       0, 1);  // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+    m_value[3] = col_type(   v[0],    v[1],    v[2], 1);  // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2],    0);  // NOLINT
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2],    0);  // NOLINT
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2],    0);  // NOLINT
+    m_value[3] = col_type(   v[0],    v[1],    v[2], v[3]);  // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------------------
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
+    TMat44<T> m;
+    m[0][0] =  2 / (right - left);
+    m[1][1] =  2 / (top   - bottom);
+    m[2][2] = -2 / (far   - near);
+    m[3][0] = -(right + left)   / (right - left);
+    m[3][1] = -(top   + bottom) / (top   - bottom);
+    m[3][2] = -(far   + near)   / (far   - near);
+    return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
+    TMat44<T> m;
+    m[0][0] =  (2 * near) / (right - left);
+    m[1][1] =  (2 * near) / (top   - bottom);
+    m[2][0] =  (right + left)   / (right - left);
+    m[2][1] =  (top   + bottom) / (top   - bottom);
+    m[2][2] = -(far   + near)   / (far   - near);
+    m[2][3] = -1;
+    m[3][2] = -(2 * far * near) / (far   - near);
+    m[3][3] =  0;
+    return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+    T h;
+    T w;
+
+    if (direction == TMat44::Fov::VERTICAL) {
+        h = std::tan(fov * M_PI / 360.0f) * near;
+        w = h * aspect;
+    } else {
+        w = std::tan(fov * M_PI / 360.0f) * near;
+        h = w / aspect;
+    }
+    return frustum(-w, w, -h, h, near, far);
+}
+
+/*
+ * Returns a matrix representing the pose of a virtual camera looking towards -Z in its
+ * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its
+ * looking at and "up" defines where the Y axis of the camera's local coordinate system is.
+ */
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) {
+    TVec3<T> z_axis(normalize(center - eye));
+    TVec3<T> norm_up(normalize(up));
+    if (std::abs(dot(z_axis, norm_up)) > 0.999) {
+        // Fix up vector if we're degenerate (looking straight up, basically)
+        norm_up = { norm_up.z, norm_up.x, norm_up.y };
+    }
+    TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
+    TVec3<T> y_axis(cross(x_axis, z_axis));
+    return TMat44<T>(
+            TVec4<T>(x_axis, 0),
+            TVec4<T>(y_axis, 0),
+            TVec4<T>(-z_axis, 0),
+            TVec4<T>(eye, 1));
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat44<T>::col_type result;
+    for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) {
+    return lhs * TVec4<U>{ rhs, 1 };
+}
+
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) {
+    typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(TMat44<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+    return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
+    return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
new file mode 100644
index 0000000..1936a2b
--- /dev/null
+++ b/libs/math/include/math/quat.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/half.h>
+#include <math/TQuatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef PURE
+#define PURE __attribute__((pure))
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TQuaternion : public TVecAddOperators<TQuaternion, T>,
+                    public TVecUnaryOperators<TQuaternion, T>,
+                    public TVecComparisonOperators<TQuaternion, T>,
+                    public TQuatProductOperators<TQuaternion, T>,
+                    public TQuatFunctions<TQuaternion, T>,
+                    public TQuatDebug<TQuaternion, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    /*
+     * quaternion internals stored as:
+     *
+     * q = w + xi + yj + zk
+     *
+     *  q[0] = x;
+     *  q[1] = y;
+     *  q[2] = z;
+     *  q[3] = w;
+     *
+     */
+    union {
+        struct { T x, y, z, w; };
+        TVec4<T> xyzw;
+        TVec3<T> xyz;
+        TVec2<T> xy;
+    };
+
+    enum { SIZE = 4 };
+    inline constexpr static size_type size() { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TQuaternion(const TQuaternion&) = default;
+    ~TQuaternion() = default;
+    TQuaternion& operator = (const TQuaternion&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {}
+
+    // default constructor. sets all values to zero.
+    constexpr TQuaternion() : x(0), y(0), z(0), w(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A>
+    constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {
+        static_assert(std::is_arithmetic<A>::value, "requires arithmetic type");
+    }
+
+    // initialize from 4 values to w + xi + yj + zk
+    template<typename A, typename B, typename C, typename D>
+    constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { }
+
+    // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
+    template<typename A, typename B>
+    constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+    // initialize from a double4
+    template<typename A>
+    constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+    // initialize from a quaternion of a different type
+    template<typename A>
+    constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+    // conjugate operator
+    constexpr TQuaternion operator~() const {
+        return conj(*this);
+    }
+
+    // constructs a quaternion from an axis and angle
+    template <typename A, typename B>
+    constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
+        return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5));
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TQuaternion<double> quatd;
+typedef details::TQuaternion<float> quat;
+typedef details::TQuaternion<float> quatf;
+typedef details::TQuaternion<half> quath;
+
+constexpr inline quat operator"" _i(long double v) {
+    return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(long double v) {
+    return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(long double v) {
+    return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quat operator"" _i(unsigned long long v) {  // NOLINT
+    return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(unsigned long long v) {  // NOLINT
+    return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(unsigned long long v) {  // NOLINT
+    return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quatd operator"" _id(long double v) {
+    return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(long double v) {
+    return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(long double v) {
+    return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+constexpr inline quatd operator"" _id(unsigned long long v) {  // NOLINT
+    return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(unsigned long long v) {  // NOLINT
+    return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(unsigned long long v) {  // NOLINT
+    return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
+
+#undef PURE
diff --git a/libs/math/include/math/scalar.h b/libs/math/include/math/scalar.h
new file mode 100644
index 0000000..2eced92
--- /dev/null
+++ b/libs/math/include/math/scalar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cmath>
+
+namespace android {
+
+template<typename T>
+static constexpr T saturate(T v) noexcept {
+    return T(std::min(T(1), std::max(T(0), v)));
+}
+
+template<typename T>
+static constexpr T clamp(T v, T min, T max) noexcept {
+    return T(std::min(max, std::max(min, v)));
+}
+
+template<typename T>
+static constexpr T mix(T x, T y, T a) noexcept {
+    return x * (T(1) - a) + y * a;
+}
+
+template<typename T>
+static constexpr T lerp(T x, T y, T a) noexcept {
+    return mix(x, y, a);
+}
+
+} // namespace std
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
new file mode 100644
index 0000000..a347633
--- /dev/null
+++ b/libs/math/include/math/vec2.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/TVecHelpers.h>
+#include <math/half.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <type_traits>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec2 :   public TVecProductOperators<TVec2, T>,
+                public TVecAddOperators<TVec2, T>,
+                public TVecUnaryOperators<TVec2, T>,
+                public TVecComparisonOperators<TVec2, T>,
+                public TVecFunctions<TVec2, T>,
+                public TVecDebug<TVec2, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y; };
+        struct { T s, t; };
+        struct { T r, g; };
+    };
+
+    static constexpr size_t SIZE = 2;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec2(const TVec2&) = default;
+    ~TVec2() = default;
+    TVec2& operator = (const TVec2&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec2(no_init) { }
+
+    // default constructor
+    constexpr TVec2() : x(0), y(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec2(A v) : x(v), y(v) { }
+
+    template<typename A, typename B>
+    constexpr TVec2(A x, B y) : x(x), y(y) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { }
+
+    // cross product works only on vectors of size 2 or 3
+    template<typename RT>
+    friend inline
+    constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
+        return value_type(u.x*v.y - u.y*v.x);
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec2<double> double2;
+typedef details::TVec2<float> float2;
+typedef details::TVec2<float> vec2;
+typedef details::TVec2<half> half2;
+typedef details::TVec2<int32_t> int2;
+typedef details::TVec2<uint32_t> uint2;
+typedef details::TVec2<int16_t> short2;
+typedef details::TVec2<uint16_t> ushort2;
+typedef details::TVec2<int8_t> byte2;
+typedef details::TVec2<uint8_t> ubyte2;
+typedef details::TVec2<bool> bool2;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
new file mode 100644
index 0000000..009fd84
--- /dev/null
+++ b/libs/math/include/math/vec3.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec2.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec3 :   public TVecProductOperators<TVec3, T>,
+                public TVecAddOperators<TVec3, T>,
+                public TVecUnaryOperators<TVec3, T>,
+                public TVecComparisonOperators<TVec3, T>,
+                public TVecFunctions<TVec3, T>,
+                public TVecDebug<TVec3, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y, z; };
+        struct { T s, t, p; };
+        struct { T r, g, b; };
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
+    };
+
+    static constexpr size_t SIZE = 3;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec3(const TVec3&) = default;
+    ~TVec3() = default;
+    TVec3& operator = (const TVec3&) = default;
+
+    // constructors
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec3(no_init) { }
+
+    // default constructor
+    constexpr TVec3() : x(0), y(0), z(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec3(A v) : x(v), y(v), z(v) { }
+
+    template<typename A, typename B, typename C>
+    constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { }
+
+    template<typename A, typename B>
+    constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
+
+    // cross product works only on vectors of size 3
+    template <typename RT>
+    friend inline
+    constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
+        return TVec3(
+                u.y*v.z - u.z*v.y,
+                u.z*v.x - u.x*v.z,
+                u.x*v.y - u.y*v.x);
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec3<double> double3;
+typedef details::TVec3<float> float3;
+typedef details::TVec3<float> vec3;
+typedef details::TVec3<half> half3;
+typedef details::TVec3<int32_t> int3;
+typedef details::TVec3<uint32_t> uint3;
+typedef details::TVec3<int16_t> short3;
+typedef details::TVec3<uint16_t> ushort3;
+typedef details::TVec3<int8_t> byte3;
+typedef details::TVec3<uint8_t> ubyte3;
+typedef details::TVec3<bool> bool3;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
new file mode 100644
index 0000000..1e279fe
--- /dev/null
+++ b/libs/math/include/math/vec4.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec3.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class  TVec4 :  public TVecProductOperators<TVec4, T>,
+                public TVecAddOperators<TVec4, T>,
+                public TVecUnaryOperators<TVec4, T>,
+                public TVecComparisonOperators<TVec4, T>,
+                public TVecFunctions<TVec4, T>,
+                public TVecDebug<TVec4, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y, z, w; };
+        struct { T s, t, p, q; };
+        struct { T r, g, b, a; };
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
+        TVec3<T> xyz;
+        TVec3<T> stp;
+        TVec3<T> rgb;
+    };
+
+    static constexpr size_t SIZE = 4;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec4(const TVec4&) = default;
+    ~TVec4() = default;
+    TVec4& operator = (const TVec4&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec4(no_init) { }
+
+    // default constructor
+    constexpr TVec4() : x(0), y(0), z(0), w(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { }
+
+    template<typename A, typename B, typename C, typename D>
+    constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+
+    template<typename A, typename B, typename C>
+    constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+
+    template<typename A, typename B>
+    constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec4<double> double4;
+typedef details::TVec4<float> float4;
+typedef details::TVec4<float> vec4;
+typedef details::TVec4<half> half4;
+typedef details::TVec4<int32_t> int4;
+typedef details::TVec4<uint32_t> uint4;
+typedef details::TVec4<int16_t> short4;
+typedef details::TVec4<uint16_t> ushort4;
+typedef details::TVec4<int8_t> byte4;
+typedef details::TVec4<uint8_t> ubyte4;
+typedef details::TVec4<bool> bool4;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
new file mode 100644
index 0000000..0ed24a2
--- /dev/null
+++ b/libs/math/tests/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "vec_test",
+    srcs: ["vec_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "mat_test",
+    srcs: ["mat_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "half_test",
+    srcs: ["half_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "quat_test",
+    srcs: ["quat_test.cpp"],
+    static_libs: ["libmath"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
new file mode 100644
index 0000000..496a7ef
--- /dev/null
+++ b/libs/math/tests/half_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HalfTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HalfTest : public testing::Test {
+protected:
+};
+
+TEST_F(HalfTest, Basics) {
+
+    EXPECT_EQ(2UL, sizeof(half));
+
+    // test +/- zero
+    EXPECT_EQ(0x0000, half( 0.0f).getBits());
+    EXPECT_EQ(0x8000, half(-0.0f).getBits());
+
+    // test nan
+    EXPECT_EQ(0x7e00, half(NAN).getBits());
+
+    // test +/- infinity
+    EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits());
+    EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits());
+
+    // test a few known values
+    EXPECT_EQ(0x3C01, half(1.0009765625).getBits());
+    EXPECT_EQ(0xC000, half(-2).getBits());
+    EXPECT_EQ(0x0400, half(6.10352e-5).getBits());
+    EXPECT_EQ(0x7BFF, half(65504).getBits());
+    EXPECT_EQ(0x3555, half(1.0f/3).getBits());
+
+    // numeric limits
+    EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits());
+    EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits());
+    EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits());
+    EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits());
+
+    // denormals (flushed to zero)
+    EXPECT_EQ(0x0000, half( 6.09756e-5).getBits());      // if handled, should be: 0x03FF
+    EXPECT_EQ(0x0000, half( 5.96046e-8).getBits());      // if handled, should be: 0x0001
+    EXPECT_EQ(0x8000, half(-6.09756e-5).getBits());      // if handled, should be: 0x83FF
+    EXPECT_EQ(0x8000, half(-5.96046e-8).getBits());      // if handled, should be: 0x8001
+
+    // test all exactly representable integers
+    for (int i=-2048 ; i<= 2048 ; ++i) {
+        half h = i;
+        EXPECT_EQ(i, float(h));
+    }
+}
+
+TEST_F(HalfTest, Literals) {
+    half one = 1.0_hf;
+    half pi = 3.1415926_hf;
+    half minusTwo = -2.0_hf;
+
+    EXPECT_EQ(half(1.0f), one);
+    EXPECT_EQ(half(3.1415926), pi);
+    EXPECT_EQ(half(-2.0f), minusTwo);
+}
+
+
+TEST_F(HalfTest, Vec) {
+    float4 f4(1,2,3,4);
+    half4 h4(f4);
+    half3 h3(f4.xyz);
+    half2 h2(f4.xy);
+
+    EXPECT_EQ(f4, h4);
+    EXPECT_EQ(f4.xyz, h3);
+    EXPECT_EQ(f4.xy, h2);
+}
+
+}; // namespace android
diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp
new file mode 100644
index 0000000..c365366
--- /dev/null
+++ b/libs/math/tests/mat_test.cpp
@@ -0,0 +1,692 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MatTest"
+
+#include <stdlib.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#include <math/mat2.h>
+#include <math/mat4.h>
+
+namespace android {
+
+class MatTest : public testing::Test {
+protected:
+};
+
+TEST_F(MatTest, Basics) {
+    mat4 m0;
+    EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
+}
+
+TEST_F(MatTest, ComparisonOps) {
+    mat4 m0;
+    mat4 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(MatTest, Constructors) {
+    mat4 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[0].z, 0);
+    ASSERT_EQ(m0[0].w, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+    ASSERT_EQ(m0[1].z, 0);
+    ASSERT_EQ(m0[1].w, 0);
+    ASSERT_EQ(m0[2].x, 0);
+    ASSERT_EQ(m0[2].y, 0);
+    ASSERT_EQ(m0[2].z, 1);
+    ASSERT_EQ(m0[2].w, 0);
+    ASSERT_EQ(m0[3].x, 0);
+    ASSERT_EQ(m0[3].y, 0);
+    ASSERT_EQ(m0[3].z, 0);
+    ASSERT_EQ(m0[3].w, 1);
+
+    mat4 m1(2);
+    mat4 m2(vec4(2));
+    mat4 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+
+    mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
+}
+
+TEST_F(MatTest, ArithmeticOps) {
+    mat4 m0;
+    mat4 m1(2);
+    mat4 m2(vec4(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat4(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat4(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat4(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat4(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat4(-1), m0);
+}
+
+TEST_F(MatTest, UnaryOps) {
+    const mat4 identity;
+    mat4 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat4(vec4(-1, 0,  0,  0),
+                   vec4(0, -1,  0,  0),
+                   vec4(0,  0, -1,  0),
+                   vec4(0,  0,  0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(MatTest, MiscOps) {
+    const mat4 identity;
+    mat4 m0;
+    EXPECT_EQ(4, trace(m0));
+
+    mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+    mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
+    mat4 m3i(inverse(m3));
+    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+    EXPECT_FLOAT_EQ(3,  m3i[0][1]);
+    EXPECT_FLOAT_EQ(3,  m3i[1][0]);
+    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+    mat4 m3ii(inverse(m3i));
+    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+    EXPECT_EQ(m1, m1*identity);
+
+
+    for (size_t c=0 ; c<4 ; c++) {
+        for (size_t r=0 ; r<4 ; r++) {
+            EXPECT_FLOAT_EQ(m1[c][r], m1(r, c));
+        }
+    }
+}
+
+TEST_F(MatTest, ElementAccess) {
+    mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+    for (size_t c=0 ; c<4 ; c++) {
+        for (size_t r=0 ; r<4 ; r++) {
+            EXPECT_FLOAT_EQ(m[c][r], m(r, c));
+        }
+    }
+
+    m(3,2) = 100;
+    EXPECT_FLOAT_EQ(m[2][3], 100);
+    EXPECT_FLOAT_EQ(m(3, 2), 100);
+}
+
+//------------------------------------------------------------------------------
+// MAT 3
+//------------------------------------------------------------------------------
+
+class Mat3Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat3Test, Basics) {
+    mat3 m0;
+    EXPECT_EQ(sizeof(mat3), sizeof(float)*9);
+}
+
+TEST_F(Mat3Test, ComparisonOps) {
+    mat3 m0;
+    mat3 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat3Test, Constructors) {
+    mat3 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[0].z, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+    ASSERT_EQ(m0[1].z, 0);
+    ASSERT_EQ(m0[2].x, 0);
+    ASSERT_EQ(m0[2].y, 0);
+    ASSERT_EQ(m0[2].z, 1);
+
+    mat3 m1(2);
+    mat3 m2(vec3(2));
+    mat3 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat3Test, ArithmeticOps) {
+    mat3 m0;
+    mat3 m1(2);
+    mat3 m2(vec3(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat3(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat3(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat3(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat3(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat3(-1), m0);
+}
+
+TEST_F(Mat3Test, UnaryOps) {
+    const mat3 identity;
+    mat3 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat3(vec3(-1, 0,  0),
+                   vec3(0, -1,  0),
+                   vec3(0,  0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat3Test, MiscOps) {
+    const mat3 identity;
+    mat3 m0;
+    EXPECT_EQ(3, trace(m0));
+
+    mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9));
+    mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec3(1, 5, 9), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1));
+    mat3 m3i(inverse(m3));
+    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+    EXPECT_FLOAT_EQ(3,  m3i[0][1]);
+    EXPECT_FLOAT_EQ(3,  m3i[1][0]);
+    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+    mat3 m3ii(inverse(m3i));
+    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+    EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MAT 2
+//------------------------------------------------------------------------------
+
+class Mat2Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat2Test, Basics) {
+    mat2 m0;
+    EXPECT_EQ(sizeof(mat2), sizeof(float)*4);
+}
+
+TEST_F(Mat2Test, ComparisonOps) {
+    mat2 m0;
+    mat2 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat2Test, Constructors) {
+    mat2 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+
+    mat2 m1(2);
+    mat2 m2(vec2(2));
+    mat2 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat2Test, ArithmeticOps) {
+    mat2 m0;
+    mat2 m1(2);
+    mat2 m2(vec2(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat2(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat2(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat2(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat2(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat2(-1), m0);
+}
+
+TEST_F(Mat2Test, UnaryOps) {
+    const mat2 identity;
+    mat2 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat2(vec2(-1, 0),
+                   vec2(0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat2Test, MiscOps) {
+    const mat2 identity;
+    mat2 m0;
+    EXPECT_EQ(2, trace(m0));
+
+    mat2 m1(vec2(1, 2), vec2(3, 4));
+    mat2 m2(vec2(1, 3), vec2(2, 4));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec2(1, 4), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MORE MATRIX TESTS
+//------------------------------------------------------------------------------
+
+template <typename T>
+class MatTestT : public ::testing::Test {
+public:
+};
+
+typedef ::testing::Types<float,float> TestMatrixValueTypes;
+
+TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes);
+
+#define TEST_MATRIX_INVERSE(MATRIX, EPSILON)                                \
+{                                                                           \
+    typedef decltype(MATRIX) MatrixType;                                    \
+    MatrixType inv1 = inverse(MATRIX);                                      \
+    MatrixType ident1 = MATRIX * inv1;                                      \
+    static const MatrixType IDENTITY;                                       \
+    for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) {               \
+        for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) {           \
+            EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON);     \
+        }                                                                   \
+    }                                                                       \
+}
+
+TYPED_TEST(MatTestT, Inverse4) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+
+    M44T m1(1,  0,  0,  0,
+            0,  1,  0,  0,
+            0,  0,  1,  0,
+            0,  0,  0,  1);
+
+    M44T m2(0,  -1,  0,  0,
+            1,  0,  0,  0,
+            0,  0,  1,  0,
+            0,  0,  0,  1);
+
+    M44T m3(1,  0,  0,  0,
+            0,  2,  0,  0,
+            0,  0,  0,  1,
+            0,  0,  -1,  0);
+
+    M44T m4(
+            4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+             -8.749647e-01,  1.456563e-01, -4.617587e-01, 3.044795e+00,
+             1.229049e-01,  9.892561e-01, 7.916244e-02, -6.737138e+00,
+             0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+    M44T m5(
+        4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+        -8.749647e-01,  1.456563e-01, -4.617587e-01, 3.044795e+00,
+        1.229049e-01,  9.892561e-01, 7.916244e-02, -6.737138e+00,
+        1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 0);
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse3) {
+    typedef ::android::details::TMat33<TypeParam> M33T;
+
+    M33T m1(1,  0,  0,
+            0,  1,  0,
+            0,  0,  1);
+
+    M33T m2(0,  -1,  0,
+            1,  0,  0,
+            0,  0,  1);
+
+    M33T m3(2,  0,  0,
+            0,  0,  1,
+            0,  -1,  0);
+
+    M33T m4(
+            4.683281e-01, 1.251189e-02, 0.000000e+00,
+            -8.749647e-01, 1.456563e-01, 0.000000e+00,
+            0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+    M33T m5(
+            4.683281e-01, 1.251189e-02, -8.834660e-01,
+           -8.749647e-01, 1.456563e-01, -4.617587e-01,
+            1.229049e-01, 9.892561e-01, 7.916244e-02);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 0);
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse2) {
+    typedef ::android::details::TMat22<TypeParam> M22T;
+
+    M22T m1(1,  0,
+            0,  1);
+
+    M22T m2(0,  -1,
+            1,  0);
+
+    M22T m3(
+            4.683281e-01, 1.251189e-02,
+            -8.749647e-01, 1.456563e-01);
+
+    M22T m4(
+            4.683281e-01, 1.251189e-02,
+           -8.749647e-01, 1.456563e-01);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+// A macro to help with vector comparisons within floating point range.
+#define EXPECT_VEC_EQ(VEC1, VEC2)                               \
+do {                                                            \
+    const decltype(VEC1) v1 = VEC1;                             \
+    const decltype(VEC2) v2 = VEC2;                             \
+    if (std::is_same<TypeParam,float>::value) {                 \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_FLOAT_EQ(v1[i], v2[i]);                      \
+        }                                                       \
+    } else if (std::is_same<TypeParam,float>::value) {          \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_DOUBLE_EQ(v1[i], v2[i]);                     \
+        }                                                       \
+    } else {                                                    \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_EQ(v1[i], v2[i]);                            \
+        }                                                       \
+    }                                                           \
+} while(0)
+
+//------------------------------------------------------------------------------
+// A macro to help with type comparisons within floating point range.
+#define ASSERT_TYPE_EQ(T1, T2)                                  \
+do {                                                            \
+    const decltype(T1) t1 = T1;                                 \
+    const decltype(T2) t2 = T2;                                 \
+    if (std::is_same<TypeParam,float>::value) {                 \
+        ASSERT_FLOAT_EQ(t1, t2);                                \
+    } else if (std::is_same<TypeParam,float>::value) {         \
+        ASSERT_DOUBLE_EQ(t1, t2);                               \
+    } else {                                                    \
+        ASSERT_EQ(t1, t2);                                      \
+    }                                                           \
+} while(0)
+
+//------------------------------------------------------------------------------
+// Test some translation stuff.
+TYPED_TEST(MatTestT, Translation4) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+
+    V4T translateBy(-7.3, 1.1, 14.4, 0.0);
+    V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0);
+    M44T translation_matrix = M44T::translate(translation);
+
+    V4T p1(9.9, 3.1, 41.1, 1.0);
+    V4T p2(-18.0, 0.0, 1.77, 1.0);
+    V4T p3(0, 0, 0, 1);
+    V4T p4(-1000, -1000, 1000, 1.0);
+
+    EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1);
+    EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2);
+    EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3);
+    EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4);
+}
+
+//------------------------------------------------------------------------------
+template <typename MATRIX>
+static void verifyOrthonormal(const MATRIX& A) {
+    typedef typename MATRIX::value_type T;
+
+    static constexpr T value_eps = T(100) * std::numeric_limits<T>::epsilon();
+
+    const MATRIX prod = A * transpose(A);
+    for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) {
+        for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) {
+            if (i == j) {
+                ASSERT_NEAR(prod[i][j], T(1), value_eps);
+            } else {
+                ASSERT_NEAR(prod[i][j], T(0), value_eps);
+            }
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_44) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+
+    std::default_random_engine generator(82828);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        verifyOrthonormal(m);
+    }
+
+    M44T m = M44T::eulerZYX(1, 2, 3);
+    verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_33) {
+
+    typedef ::android::details::TMat33<TypeParam> M33T;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        verifyOrthonormal(m);
+    }
+
+    M33T m = M33T::eulerZYX(1, 2, 3);
+    verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPostTranslation) {
+
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1));
+        QuatT qr = r.toQuaternion();
+        M44T tr = t * r;
+        QuatT qtr = tr.toQuaternion();
+
+        ASSERT_TYPE_EQ(qr.x, qtr.x);
+        ASSERT_TYPE_EQ(qr.y, qtr.y);
+        ASSERT_TYPE_EQ(qr.z, qtr.z);
+        ASSERT_TYPE_EQ(qr.w, qtr.w);
+    }
+
+    M44T r = M44T::eulerZYX(1, 2, 3);
+    M44T t = M44T::translate(V4T(20, -15, 2, 1));
+    QuatT qr = r.toQuaternion();
+    M44T tr = t * r;
+    QuatT qtr = tr.toQuaternion();
+
+    ASSERT_TYPE_EQ(qr.x, qtr.x);
+    ASSERT_TYPE_EQ(qr.y, qtr.y);
+    ASSERT_TYPE_EQ(qr.z, qtr.z);
+    ASSERT_TYPE_EQ(qr.w, qtr.w);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) {
+    static constexpr TypeParam value_eps =
+            TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+    typedef ::android::details::TMat33<TypeParam> M33T;
+    typedef ::android::details::TVec3<TypeParam> V3T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        QuatT qr = r.toQuaternion();
+        V3T p(rand_gen(), rand_gen(), rand_gen());
+
+        V3T pr = r * p;
+        V3T pq = qr * p;
+
+        ASSERT_NEAR(pr.x, pq.x, value_eps);
+        ASSERT_NEAR(pr.y, pq.y, value_eps);
+        ASSERT_NEAR(pr.z, pq.z, value_eps);
+    }
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) {
+    static constexpr TypeParam value_eps =
+            TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+    typedef ::android::details::TVec3<TypeParam> V3T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(992626);
+    std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        QuatT qr = r.toQuaternion();
+        V3T p(rand_gen(), rand_gen(), rand_gen());
+
+        V4T pr = r * V4T(p.x, p.y, p.z, 1);
+        pr.x /= pr.w;
+        pr.y /= pr.w;
+        pr.z /= pr.w;
+        V3T pq = qr * p;
+
+        ASSERT_NEAR(pr.x, pq.x, value_eps);
+        ASSERT_NEAR(pr.y, pq.y, value_eps);
+        ASSERT_NEAR(pr.z, pq.z, value_eps);
+    }
+}
+
+#undef TEST_MATRIX_INVERSE
+
+}; // namespace android
diff --git a/libs/math/tests/quat_test.cpp b/libs/math/tests/quat_test.cpp
new file mode 100644
index 0000000..c20771e
--- /dev/null
+++ b/libs/math/tests/quat_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "QuatTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <random>
+#include <functional>
+
+#include <math/quat.h>
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class QuatTest : public testing::Test {
+protected:
+};
+
+TEST_F(QuatTest, Basics) {
+    quatd q;
+    double4& v(q.xyzw);
+
+    EXPECT_EQ(sizeof(quatd), sizeof(double)*4);
+    EXPECT_EQ(reinterpret_cast<void*>(&q), reinterpret_cast<void*>(&v));
+}
+
+TEST_F(QuatTest, Constructors) {
+    quatd q0;
+    EXPECT_EQ(q0.x, 0);
+    EXPECT_EQ(q0.y, 0);
+    EXPECT_EQ(q0.z, 0);
+    EXPECT_EQ(q0.w, 0);
+
+    quatd q1(1);
+    EXPECT_EQ(q1.x, 0);
+    EXPECT_EQ(q1.y, 0);
+    EXPECT_EQ(q1.z, 0);
+    EXPECT_EQ(q1.w, 1);
+
+    quatd q2(1, 2, 3, 4);
+    EXPECT_EQ(q2.x, 2);
+    EXPECT_EQ(q2.y, 3);
+    EXPECT_EQ(q2.z, 4);
+    EXPECT_EQ(q2.w, 1);
+
+    quatd q3(q2);
+    EXPECT_EQ(q3.x, 2);
+    EXPECT_EQ(q3.y, 3);
+    EXPECT_EQ(q3.z, 4);
+    EXPECT_EQ(q3.w, 1);
+
+    quatd q4(q3.xyz, 42);
+    EXPECT_EQ(q4.x, 2);
+    EXPECT_EQ(q4.y, 3);
+    EXPECT_EQ(q4.z, 4);
+    EXPECT_EQ(q4.w, 42);
+
+    quatd q5(double3(q2.xy, 42), 24);
+    EXPECT_EQ(q5.x, 2);
+    EXPECT_EQ(q5.y, 3);
+    EXPECT_EQ(q5.z, 42);
+    EXPECT_EQ(q5.w, 24);
+
+    quatd q6;
+    q6 = 12;
+    EXPECT_EQ(q6.x, 0);
+    EXPECT_EQ(q6.y, 0);
+    EXPECT_EQ(q6.z, 0);
+    EXPECT_EQ(q6.w, 12);
+
+    quatd q7 = 1 + 2_id + 3_jd + 4_kd;
+    EXPECT_EQ(q7.x, 2);
+    EXPECT_EQ(q7.y, 3);
+    EXPECT_EQ(q7.z, 4);
+    EXPECT_EQ(q7.w, 1);
+
+    quatf qf(2);
+    EXPECT_EQ(qf.x, 0);
+    EXPECT_EQ(qf.y, 0);
+    EXPECT_EQ(qf.z, 0);
+    EXPECT_EQ(qf.w, 2);
+}
+
+TEST_F(QuatTest, Access) {
+    quatd q0(1, 2, 3, 4);
+    q0.x = 10;
+    q0.y = 20;
+    q0.z = 30;
+    q0.w = 40;
+    EXPECT_EQ(q0.x, 10);
+    EXPECT_EQ(q0.y, 20);
+    EXPECT_EQ(q0.z, 30);
+    EXPECT_EQ(q0.w, 40);
+
+    q0[0] = 100;
+    q0[1] = 200;
+    q0[2] = 300;
+    q0[3] = 400;
+    EXPECT_EQ(q0.x, 100);
+    EXPECT_EQ(q0.y, 200);
+    EXPECT_EQ(q0.z, 300);
+    EXPECT_EQ(q0.w, 400);
+
+    q0.xyz = double3(1, 2, 3);
+    EXPECT_EQ(q0.x, 1);
+    EXPECT_EQ(q0.y, 2);
+    EXPECT_EQ(q0.z, 3);
+    EXPECT_EQ(q0.w, 400);
+}
+
+TEST_F(QuatTest, UnaryOps) {
+    quatd q0(1, 2, 3, 4);
+
+    q0 += 1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 2);
+
+    q0 -= 1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    q0 *= 2;
+    EXPECT_EQ(q0.x, 4);
+    EXPECT_EQ(q0.y, 6);
+    EXPECT_EQ(q0.z, 8);
+    EXPECT_EQ(q0.w, 2);
+
+    q0 /= 2;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    quatd q1(10, 20, 30, 40);
+
+    q0 += q1;
+    EXPECT_EQ(q0.x, 22);
+    EXPECT_EQ(q0.y, 33);
+    EXPECT_EQ(q0.z, 44);
+    EXPECT_EQ(q0.w, 11);
+
+    q0 -= q1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    q1 = -q1;
+    EXPECT_EQ(q1.x, -20);
+    EXPECT_EQ(q1.y, -30);
+    EXPECT_EQ(q1.z, -40);
+    EXPECT_EQ(q1.w, -10);
+
+    // TODO(mathias): multiplies
+}
+
+TEST_F(QuatTest, ComparisonOps) {
+    quatd q0(1, 2, 3, 4);
+    quatd q1(10, 20, 30, 40);
+
+    EXPECT_TRUE(q0 == q0);
+    EXPECT_TRUE(q0 != q1);
+    EXPECT_FALSE(q0 != q0);
+    EXPECT_FALSE(q0 == q1);
+}
+
+TEST_F(QuatTest, ArithmeticOps) {
+    quatd q0(1, 2, 3, 4);
+    quatd q1(10, 20, 30, 40);
+
+    quatd q2(q0 + q1);
+    EXPECT_EQ(q2.x, 22);
+    EXPECT_EQ(q2.y, 33);
+    EXPECT_EQ(q2.z, 44);
+    EXPECT_EQ(q2.w, 11);
+
+    q0 = q1 * 2;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    q0 = 2 * q1;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    quatf qf(2);
+    q0 = q1 * qf;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    EXPECT_EQ(1_id * 1_id, quat(-1));
+    EXPECT_EQ(1_jd * 1_jd, quat(-1));
+    EXPECT_EQ(1_kd * 1_kd, quat(-1));
+    EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1));
+}
+
+TEST_F(QuatTest, ArithmeticFunc) {
+    quatd q(1, 2, 3, 4);
+    quatd qc(conj(q));
+    __attribute__((unused)) quatd qi(inverse(q));
+    quatd qn(normalize(q));
+
+    EXPECT_EQ(qc.x, -2);
+    EXPECT_EQ(qc.y, -3);
+    EXPECT_EQ(qc.z, -4);
+    EXPECT_EQ(qc.w,  1);
+
+    EXPECT_EQ(~q, qc);
+    EXPECT_EQ(length(q), length(qc));
+    EXPECT_EQ(sqrt(30), length(q));
+    EXPECT_FLOAT_EQ(1, length(qn));
+    EXPECT_FLOAT_EQ(1, dot(qn, qn));
+
+    quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+    EXPECT_EQ(mat4d(qr).toQuaternion(), qr);
+    EXPECT_EQ(1_id, mat4d(1_id).toQuaternion());
+    EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion());
+    EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion());
+
+
+    EXPECT_EQ(qr, log(exp(qr)));
+
+    quatd qq = qr * qr;
+    quatd q2 = pow(qr, 2);
+    EXPECT_NEAR(qq.x, q2.x, 1e-15);
+    EXPECT_NEAR(qq.y, q2.y, 1e-15);
+    EXPECT_NEAR(qq.z, q2.z, 1e-15);
+    EXPECT_NEAR(qq.w, q2.w, 1e-15);
+
+    quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0);
+    quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+    quatd qs = slerp(qa, qb, 0.5);
+    qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4);
+    EXPECT_FLOAT_EQ(qr.x, qs.x);
+    EXPECT_FLOAT_EQ(qr.y, qs.y);
+    EXPECT_FLOAT_EQ(qr.z, qs.z);
+    EXPECT_FLOAT_EQ(qr.w, qs.w);
+
+    qs = nlerp(qa, qb, 0.5);
+    EXPECT_FLOAT_EQ(qr.x, qs.x);
+    EXPECT_FLOAT_EQ(qr.y, qs.y);
+    EXPECT_FLOAT_EQ(qr.z, qs.z);
+    EXPECT_FLOAT_EQ(qr.w, qs.w);
+}
+
+TEST_F(QuatTest, MultiplicationExhaustive) {
+    static constexpr double value_eps = double(1000) * std::numeric_limits<double>::epsilon();
+
+    std::default_random_engine generator(171717);
+    std::uniform_real_distribution<double> distribution(-10.0, 10.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < (1024 * 1024); ++i) {
+        double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+        double angle_a = rand_gen();
+        quatd a = quatd::fromAxisAngle(axis_a, angle_a);
+
+        double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+        double angle_b = rand_gen();
+        quatd b = quatd::fromAxisAngle(axis_b, angle_b);
+
+        quatd ab = a * b;
+        quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz),
+            (a.w * b.w) - dot(a.xyz, b.xyz));
+
+        ASSERT_NEAR(ab.x, ab_other.x, value_eps);
+        ASSERT_NEAR(ab.y, ab_other.y, value_eps);
+        ASSERT_NEAR(ab.z, ab_other.z, value_eps);
+        ASSERT_NEAR(ab.w, ab_other.w, value_eps);
+    }
+}
+
+}; // namespace android
diff --git a/libs/math/tests/vec_test.cpp b/libs/math/tests/vec_test.cpp
new file mode 100644
index 0000000..79ae2e4
--- /dev/null
+++ b/libs/math/tests/vec_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VecTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class VecTest : public testing::Test {
+};
+
+TEST_F(VecTest, Basics) {
+    vec4 v4;
+    vec3& v3(v4.xyz);
+
+    EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
+    EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
+    EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
+    EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4));
+}
+
+TEST_F(VecTest, Constructors) {
+    vec4 v0;
+    EXPECT_EQ(v0.x, 0);
+    EXPECT_EQ(v0.y, 0);
+    EXPECT_EQ(v0.z, 0);
+    EXPECT_EQ(v0.w, 0);
+
+    vec4 v1(1);
+    EXPECT_EQ(v1.x, 1);
+    EXPECT_EQ(v1.y, 1);
+    EXPECT_EQ(v1.z, 1);
+    EXPECT_EQ(v1.w, 1);
+
+    vec4 v2(1, 2, 3, 4);
+    EXPECT_EQ(v2.x, 1);
+    EXPECT_EQ(v2.y, 2);
+    EXPECT_EQ(v2.z, 3);
+    EXPECT_EQ(v2.w, 4);
+
+    vec4 v3(v2);
+    EXPECT_EQ(v3.x, 1);
+    EXPECT_EQ(v3.y, 2);
+    EXPECT_EQ(v3.z, 3);
+    EXPECT_EQ(v3.w, 4);
+
+    vec4 v4(v3.xyz, 42);
+    EXPECT_EQ(v4.x, 1);
+    EXPECT_EQ(v4.y, 2);
+    EXPECT_EQ(v4.z, 3);
+    EXPECT_EQ(v4.w, 42);
+
+    vec4 v5(vec3(v2.xy, 42), 24);
+    EXPECT_EQ(v5.x, 1);
+    EXPECT_EQ(v5.y, 2);
+    EXPECT_EQ(v5.z, 42);
+    EXPECT_EQ(v5.w, 24);
+
+    float4 vf(2);
+    EXPECT_EQ(vf.x, 2);
+    EXPECT_EQ(vf.y, 2);
+    EXPECT_EQ(vf.z, 2);
+    EXPECT_EQ(vf.w, 2);
+}
+
+TEST_F(VecTest, Access) {
+    vec4 v0(1, 2, 3, 4);
+
+    v0.x = 10;
+    v0.y = 20;
+    v0.z = 30;
+    v0.w = 40;
+    EXPECT_EQ(v0.x, 10);
+    EXPECT_EQ(v0.y, 20);
+    EXPECT_EQ(v0.z, 30);
+    EXPECT_EQ(v0.w, 40);
+
+    v0[0] = 100;
+    v0[1] = 200;
+    v0[2] = 300;
+    v0[3] = 400;
+    EXPECT_EQ(v0.x, 100);
+    EXPECT_EQ(v0.y, 200);
+    EXPECT_EQ(v0.z, 300);
+    EXPECT_EQ(v0.w, 400);
+
+    v0.xyz = vec3(1, 2, 3);
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 400);
+}
+
+TEST_F(VecTest, UnaryOps) {
+    vec4 v0(1, 2, 3, 4);
+
+    v0 += 1;
+    EXPECT_EQ(v0.x, 2);
+    EXPECT_EQ(v0.y, 3);
+    EXPECT_EQ(v0.z, 4);
+    EXPECT_EQ(v0.w, 5);
+
+    v0 -= 1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v0 *= 2;
+    EXPECT_EQ(v0.x, 2);
+    EXPECT_EQ(v0.y, 4);
+    EXPECT_EQ(v0.z, 6);
+    EXPECT_EQ(v0.w, 8);
+
+    v0 /= 2;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    vec4 v1(10, 20, 30, 40);
+
+    v0 += v1;
+    EXPECT_EQ(v0.x, 11);
+    EXPECT_EQ(v0.y, 22);
+    EXPECT_EQ(v0.z, 33);
+    EXPECT_EQ(v0.w, 44);
+
+    v0 -= v1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v0 *= v1;
+    EXPECT_EQ(v0.x, 10);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 90);
+    EXPECT_EQ(v0.w, 160);
+
+    v0 /= v1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v1 = -v1;
+    EXPECT_EQ(v1.x, -10);
+    EXPECT_EQ(v1.y, -20);
+    EXPECT_EQ(v1.z, -30);
+    EXPECT_EQ(v1.w, -40);
+
+    float4 fv(1, 2, 3, 4);
+    v1 += fv;
+    EXPECT_EQ(v1.x,  -9);
+    EXPECT_EQ(v1.y, -18);
+    EXPECT_EQ(v1.z, -27);
+    EXPECT_EQ(v1.w, -36);
+}
+
+TEST_F(VecTest, ComparisonOps) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    EXPECT_TRUE(v0 == v0);
+    EXPECT_TRUE(v0 != v1);
+    EXPECT_FALSE(v0 != v0);
+    EXPECT_FALSE(v0 == v1);
+}
+
+TEST_F(VecTest, ComparisonFunctions) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    EXPECT_TRUE(all(equal(v0, v0)));
+    EXPECT_TRUE(all(notEqual(v0, v1)));
+    EXPECT_FALSE(any(notEqual(v0, v0)));
+    EXPECT_FALSE(any(equal(v0, v1)));
+
+    EXPECT_FALSE(all(lessThan(v0, v0)));
+    EXPECT_TRUE(all(lessThanEqual(v0, v0)));
+    EXPECT_FALSE(all(greaterThan(v0, v0)));
+    EXPECT_TRUE(all(greaterThanEqual(v0, v0)));
+    EXPECT_TRUE(all(lessThan(v0, v1)));
+    EXPECT_TRUE(all(greaterThan(v1, v0)));
+}
+
+TEST_F(VecTest, ArithmeticOps) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    vec4 v2(v0 + v1);
+    EXPECT_EQ(v2.x, 11);
+    EXPECT_EQ(v2.y, 22);
+    EXPECT_EQ(v2.z, 33);
+    EXPECT_EQ(v2.w, 44);
+
+    v0 = v1 * 2;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+
+    v0 = 2 * v1;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+
+    float4 vf(2);
+    v0 = v1 * vf;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+}
+
+TEST_F(VecTest, ArithmeticFunc) {
+    vec3 east(1, 0, 0);
+    vec3 north(0, 1, 0);
+    vec3 up(cross(east, north));
+    EXPECT_EQ(up, vec3(0, 0, 1));
+    EXPECT_EQ(dot(east, north), 0);
+    EXPECT_EQ(length(east), 1);
+    EXPECT_EQ(distance(east, north), sqrtf(2));
+
+    vec3 v0(1, 2, 3);
+    vec3 vn(normalize(v0));
+    EXPECT_FLOAT_EQ(1, length(vn));
+    EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
+
+    float3 vf(east);
+    EXPECT_EQ(length(vf), 1);
+
+    EXPECT_TRUE(any(vec3(0, 0, 1)));
+    EXPECT_FALSE(any(vec3(0, 0, 0)));
+
+    EXPECT_TRUE(all(vec3(1, 1, 1)));
+    EXPECT_FALSE(all(vec3(0, 0, 1)));
+
+    EXPECT_TRUE(any(bool3(false, false, true)));
+    EXPECT_FALSE(any(bool3(false)));
+
+    EXPECT_TRUE(all(bool3(true)));
+    EXPECT_FALSE(all(bool3(false, false, true)));
+
+    std::function<bool(float)> p = [](auto v) -> bool { return v > 0.0f; };
+    EXPECT_TRUE(all(map(vec3(1, 2, 3), p)));
+}
+
+}; // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
new file mode 100644
index 0000000..ed292e7
--- /dev/null
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer"
+
+#include <vndk/hardware_buffer.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <memory>
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include <ui/GraphicBuffer.h>
+#include <system/graphics.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+
+static constexpr int kFdBufferSize = 128 * sizeof(int);  // 128 ints
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Public functions
+// ----------------------------------------------------------------------------
+
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
+    if (!outBuffer || !desc)
+        return BAD_VALUE;
+
+    if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+        ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
+        return BAD_VALUE;
+    }
+
+    int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+    if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+        ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
+        return BAD_VALUE;
+    }
+
+    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
+        ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
+        return BAD_VALUE;
+    }
+
+    uint64_t usage =  AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+    sp<GraphicBuffer> gbuffer(new GraphicBuffer(
+            desc->width, desc->height, format, desc->layers, usage,
+            std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
+
+    status_t err = gbuffer->initCheck();
+    if (err != 0 || gbuffer->handle == 0) {
+        if (err == NO_MEMORY) {
+            GraphicBuffer::dumpAllocationsToSystemLog();
+        }
+        ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
+                desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle);
+        return err;
+    }
+
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+    // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+    AHardwareBuffer_acquire(*outBuffer);
+    return NO_ERROR;
+}
+
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong((void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_release(AHardwareBuffer* buffer) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong((void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+        AHardwareBuffer_Desc* outDesc) {
+    if (!buffer || !outDesc) return;
+
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    outDesc->width = gbuffer->getWidth();
+    outDesc->height = gbuffer->getHeight();
+    outDesc->layers = gbuffer->getLayerCount();
+    outDesc->format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gbuffer->getPixelFormat()));
+    outDesc->usage = AHardwareBuffer_convertFromGrallocUsageBits(gbuffer->getUsage());
+    outDesc->stride = gbuffer->getStride();
+    outDesc->rfu0 = 0;
+    outDesc->rfu1 = 0;
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
+        int32_t fence, const ARect* rect, void** outVirtualAddress) {
+    if (!buffer) return BAD_VALUE;
+
+    if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+                  AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+        ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+                " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+        return BAD_VALUE;
+    }
+
+    usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    Rect bounds;
+    if (!rect) {
+        bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+    } else {
+        bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+    }
+    return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+}
+
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
+    if (!buffer) return BAD_VALUE;
+
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    if (fence == nullptr)
+        return gBuffer->unlock();
+    else
+        return gBuffer->unlockAsync(fence);
+}
+
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) {
+    if (!buffer) return BAD_VALUE;
+    const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    size_t flattenedSize = gBuffer->getFlattenedSize();
+    size_t fdCount = gBuffer->getFdCount();
+
+    std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]);
+    std::unique_ptr<int[]> fds(new int[fdCount]);
+
+    // Make copies of needed items since flatten modifies them, and we don't
+    // want to send anything if there's an error during flatten.
+    size_t flattenedSizeCopy = flattenedSize;
+    size_t fdCountCopy = fdCount;
+    void* dataStart = data.get();
+    int* fdsStart = fds.get();
+    status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart,
+            fdCountCopy);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    struct iovec iov[1];
+    iov[0].iov_base = data.get();
+    iov[0].iov_len = flattenedSize;
+
+    char buf[CMSG_SPACE(kFdBufferSize)];
+    struct msghdr msg = {
+            .msg_control = buf,
+            .msg_controllen = sizeof(buf),
+            .msg_iov = &iov[0],
+            .msg_iovlen = 1,
+    };
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
+    int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    memcpy(fdData, fds.get(), sizeof(int) * fdCount);
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    int result;
+    do {
+        result = sendmsg(socketFd, &msg, 0);
+    } while (result == -1 && errno == EINTR);
+    if (result == -1) {
+        result = errno;
+        ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
+                result, strerror(result));
+        return -result;
+    }
+
+    return NO_ERROR;
+}
+
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) {
+    if (!outBuffer) return BAD_VALUE;
+
+    static constexpr int kMessageBufferSize = 4096 * sizeof(int);
+
+    std::unique_ptr<char[]> dataBuf(new char[kMessageBufferSize]);
+    char fdBuf[CMSG_SPACE(kFdBufferSize)];
+    struct iovec iov[1];
+    iov[0].iov_base = dataBuf.get();
+    iov[0].iov_len = kMessageBufferSize;
+
+    struct msghdr msg = {
+            .msg_control = fdBuf,
+            .msg_controllen = sizeof(fdBuf),
+            .msg_iov = &iov[0],
+            .msg_iovlen = 1,
+    };
+
+    int result;
+    do {
+        result = recvmsg(socketFd, &msg, 0);
+    } while (result == -1 && errno == EINTR);
+    if (result == -1) {
+        result = errno;
+        ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
+                result, strerror(result));
+        return -result;
+    }
+
+    if (msg.msg_iovlen != 1) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad data length");
+        return INVALID_OPERATION;
+    }
+
+    if (msg.msg_controllen % sizeof(int) != 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad fd length");
+        return INVALID_OPERATION;
+    }
+
+    size_t dataLen = msg.msg_iov[0].iov_len;
+    const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base);
+    if (!data) {
+        ALOGE("Error reading AHardwareBuffer from socket: no buffer data");
+        return INVALID_OPERATION;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    if (!cmsg) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd header");
+        return INVALID_OPERATION;
+    }
+
+    size_t fdCount = msg.msg_controllen >> 2;
+    const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+    if (!fdData) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd data");
+        return INVALID_OPERATION;
+    }
+
+    GraphicBuffer* gBuffer = new GraphicBuffer();
+    status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer);
+    // Ensure the buffer has a positive ref-count.
+    AHardwareBuffer_acquire(*outBuffer);
+
+    return NO_ERROR;
+}
+
+
+// ----------------------------------------------------------------------------
+// VNDK functions
+// ----------------------------------------------------------------------------
+
+const native_handle_t* AHardwareBuffer_getNativeHandle(
+        const AHardwareBuffer* buffer) {
+    if (!buffer) return nullptr;
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return gbuffer->handle;
+}
+
+
+// ----------------------------------------------------------------------------
+// Helpers implementation
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
+struct UsageMaskMapping {
+    uint64_t hardwareBufferMask;
+    uint64_t grallocMask;
+};
+
+static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
+    return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+}
+
+bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
+    static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_422_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_444_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_FLEX_RGB_888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_FLEX_RGBA_8888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
+            "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
+            "HAL and AHardwareBuffer pixel format don't match");
+
+    switch (format) {
+        case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+        case AHARDWAREBUFFER_FORMAT_BLOB:
+            // VNDK formats only -- unfortunately we can't differentiate from where we're called
+        case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
+        case AHARDWAREBUFFER_FORMAT_YV12:
+        case AHARDWAREBUFFER_FORMAT_Y8:
+        case AHARDWAREBUFFER_FORMAT_Y16:
+        case AHARDWAREBUFFER_FORMAT_RAW16:
+        case AHARDWAREBUFFER_FORMAT_RAW10:
+        case AHARDWAREBUFFER_FORMAT_RAW12:
+        case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
+        case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422:
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444:
+        case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8:
+        case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
+        case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
+    return hal_format;
+}
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) {
+    return ahardwarebuffer_format;
+}
+
+uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
+    using android::hardware::graphics::common::V1_0::BufferUsage;
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_RARELY == (uint64_t)BufferUsage::CPU_READ_RARELY,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN == (uint64_t)BufferUsage::CPU_READ_OFTEN,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER == (uint64_t)BufferUsage::CPU_WRITE_NEVER,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY == (uint64_t)BufferUsage::CPU_WRITE_RARELY,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN == (uint64_t)BufferUsage::CPU_WRITE_OFTEN,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_VIDEO_ENCODE == (uint64_t)BufferUsage::VIDEO_ENCODER,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER == (uint64_t)BufferUsage::GPU_DATA_BUFFER,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA == (uint64_t)BufferUsage::SENSOR_DIRECT_DATA,
+            "gralloc and AHardwareBuffer flags don't match");
+    return usage;
+}
+
+uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) {
+    return usage;
+}
+
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) {
+    return reinterpret_cast<const GraphicBuffer*>(buffer);
+}
+
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) {
+    return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) {
+    return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer) {
+    return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) {
+    return reinterpret_cast<AHardwareBuffer*>(buffer);
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
new file mode 100644
index 0000000..f64bab1
--- /dev/null
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ANativeWindow"
+
+#include <android/native_window.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
+// from nativewindow/includes/system/window.h
+// (not to be confused with the compatibility-only window.h from system/core/includes)
+#include <system/window.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+static int32_t query(ANativeWindow* window, int what) {
+    int value;
+    int res = window->query(window, what, &value);
+    return res < 0 ? res : value;
+}
+
+/**************************************************************************************************
+ * NDK
+ **************************************************************************************************/
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    window->decStrong((void*)ANativeWindow_acquire);
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+    return query(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+    return query(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+    return query(window, NATIVE_WINDOW_FORMAT);
+}
+
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
+        int32_t width, int32_t height, int32_t format) {
+    int32_t err = native_window_set_buffers_format(window, format);
+    if (!err) {
+        err = native_window_set_buffers_user_dimensions(window, width, height);
+        if (!err) {
+            int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+            if (width && height) {
+                mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+            }
+            err = native_window_set_scaling_mode(window, mode);
+        }
+    }
+    return err;
+}
+
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+        ARect* inOutDirtyBounds) {
+    return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds);
+}
+
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
+    return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST);
+}
+
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) {
+    static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL == NATIVE_WINDOW_TRANSFORM_FLIP_H);
+    static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL == NATIVE_WINDOW_TRANSFORM_FLIP_V);
+    static_assert(ANATIVEWINDOW_TRANSFORM_ROTATE_90 == NATIVE_WINDOW_TRANSFORM_ROT_90);
+
+    constexpr int32_t kAllTransformBits =
+            ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+            ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
+            ANATIVEWINDOW_TRANSFORM_ROTATE_90;
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID))
+        return -EINVAL;
+    if ((transform & ~kAllTransformBits) != 0)
+        return -EINVAL;
+
+    return native_window_set_buffers_transform(window, transform);
+}
+
+/**************************************************************************************************
+ * vndk-stable
+ **************************************************************************************************/
+
+AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb) {
+    return AHardwareBuffer_from_GraphicBuffer(static_cast<GraphicBuffer*>(anwb));
+}
+
+int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value) {
+    if (slot < 4) {
+        window->oem[slot] = value;
+        return 0;
+    }
+    return -EINVAL;
+}
+
+int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value) {
+    if (slot >= 4) {
+        *value = window->oem[slot];
+        return 0;
+    }
+    return -EINVAL;
+}
+
+
+int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval) {
+    return window->setSwapInterval(window, interval);
+}
+
+int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery what, int* value) {
+    switch (what) {
+        case ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS:
+        case ANATIVEWINDOW_QUERY_DEFAULT_WIDTH:
+        case ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT:
+        case ANATIVEWINDOW_QUERY_TRANSFORM_HINT:
+            // these are part of the VNDK API
+            break;
+        case ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL:
+            *value = window->minSwapInterval;
+            return 0;
+        case ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL:
+            *value = window->maxSwapInterval;
+            return 0;
+        case ANATIVEWINDOW_QUERY_XDPI:
+            *value = (int)window->xdpi;
+            return 0;
+        case ANATIVEWINDOW_QUERY_YDPI:
+            *value = (int)window->ydpi;
+            return 0;
+        default:
+            // asked for an invalid query(), one that isn't part of the VNDK
+            return -EINVAL;
+    }
+    return window->query(window, int(what), value);
+}
+
+int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery what, float* value) {
+    switch (what) {
+        case ANATIVEWINDOW_QUERY_XDPI:
+            *value = window->xdpi;
+            return 0;
+        case ANATIVEWINDOW_QUERY_YDPI:
+            *value = window->ydpi;
+            return 0;
+        default:
+            break;
+    }
+
+    int i;
+    int e = ANativeWindow_query(window, what, &i);
+    if (e == 0) {
+        *value = (float)i;
+    }
+    return e;
+}
+
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+    return window->dequeueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    return window->queueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    return window->cancelBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage) {
+    usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+    return native_window_set_usage(window, (uint32_t)usage); // FIXME: we need a 64-bits version
+}
+
+int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount) {
+    return native_window_set_buffer_count(window, bufferCount);
+}
+
+int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h) {
+    return native_window_set_buffers_dimensions(window, (int)w, (int)h);
+}
+
+int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format) {
+    return native_window_set_buffers_format(window, format);
+}
+
+int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp) {
+    return native_window_set_buffers_timestamp(window, timestamp);
+}
+
+int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace) {
+    return native_window_set_buffers_data_space(window, dataSpace);
+}
+
+int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode) {
+    return native_window_set_shared_buffer_mode(window, sharedBufferMode);
+}
+
+int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) {
+    return native_window_set_auto_refresh(window, autoRefresh);
+}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
new file mode 100644
index 0000000..d759acb
--- /dev/null
+++ b/libs/nativewindow/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libnativewindow_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "libnativewindow",
+    symbol_file: "libnativewindow.map.txt",
+
+    // Android O
+    first_version: "26",
+}
+
+cc_library {
+    name: "libnativewindow",
+    export_include_dirs: ["include"],
+
+    clang: true,
+
+    cppflags: [
+        "-std=c++1z"
+    ],
+
+    srcs: [
+        "AHardwareBuffer.cpp",
+        "ANativeWindow.cpp",
+    ],
+
+    shared_libs: [
+        "libhardware",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libui",
+        "android.hardware.graphics.common@1.0",
+    ],
+
+    static_libs: [
+        "libarect",
+        "libgrallocusage",
+    ],
+
+    // headers we include in our public headers
+    export_static_lib_headers: [
+        "libarect",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/nativewindow/MODULE_LICENSE_APACHE2 b/libs/nativewindow/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativewindow/MODULE_LICENSE_APACHE2
diff --git a/libs/nativewindow/NOTICE b/libs/nativewindow/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativewindow/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
new file mode 100644
index 0000000..52440a5
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file hardware_buffer.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_H
+#define ANDROID_HARDWARE_BUFFER_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+#include <android/rect.h>
+
+__BEGIN_DECLS
+
+/**
+ * Buffer pixel formats.
+ */
+enum {
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+     *   OpenGL ES: GL_RGBA8
+     */
+    AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM           = 1,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+     *   OpenGL ES: GL_RGBA8
+     */
+    AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM           = 2,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R8G8B8_UNORM
+     *   OpenGL ES: GL_RGB8
+     */
+    AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM             = 3,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R5G6B5_UNORM_PACK16
+     *   OpenGL ES: GL_RGB565
+     */
+    AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM             = 4,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT
+     *   OpenGL ES: GL_RGBA16F
+     */
+    AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT       = 0x16,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_A2B10G10R10_UNORM_PACK32
+     *   OpenGL ES: GL_RGB10_A2
+     */
+    AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM        = 0x2b,
+
+    /**
+     * An opaque binary blob format that must have height 1, with width equal to
+     * the buffer size in bytes.
+     */
+    AHARDWAREBUFFER_FORMAT_BLOB                     = 0x21,
+};
+
+enum {
+    /* The buffer will never be read by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_READ_NEVER        = 0UL,
+    /* The buffer will sometimes be read by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_READ_RARELY       = 2UL,
+    /* The buffer will often be read by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN        = 3UL,
+    /* CPU read value mask */
+    AHARDWAREBUFFER_USAGE_CPU_READ_MASK         = 0xFUL,
+
+    /* The buffer will never be written by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER       = 0UL << 4,
+    /* The buffer will sometimes be written to by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY      = 2UL << 4,
+    /* The buffer will often be written to by the CPU */
+    AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN       = 3UL << 4,
+    /* CPU write value mask */
+    AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK        = 0xFUL << 4,
+
+    /* The buffer will be read from by the GPU */
+    AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE      = 1UL << 8,
+    /* The buffer will be written to by the GPU */
+    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = 1UL << 9,
+    /* The buffer must not be used outside of a protected hardware path */
+    AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT      = 1UL << 14,
+    /* The buffer will be read by a hardware video encoder */
+    AHARDWAREBUFFER_USAGE_VIDEO_ENCODE           = 1UL << 16,
+    /** The buffer will be used for sensor direct data */
+    AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA     = 1UL << 23,
+    /* The buffer will be used as a shader storage or uniform buffer object*/
+    AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER        = 1UL << 24,
+
+    AHARDWAREBUFFER_USAGE_VENDOR_0  = 1ULL << 28,
+    AHARDWAREBUFFER_USAGE_VENDOR_1  = 1ULL << 29,
+    AHARDWAREBUFFER_USAGE_VENDOR_2  = 1ULL << 30,
+    AHARDWAREBUFFER_USAGE_VENDOR_3  = 1ULL << 31,
+    AHARDWAREBUFFER_USAGE_VENDOR_4  = 1ULL << 48,
+    AHARDWAREBUFFER_USAGE_VENDOR_5  = 1ULL << 49,
+    AHARDWAREBUFFER_USAGE_VENDOR_6  = 1ULL << 50,
+    AHARDWAREBUFFER_USAGE_VENDOR_7  = 1ULL << 51,
+    AHARDWAREBUFFER_USAGE_VENDOR_8  = 1ULL << 52,
+    AHARDWAREBUFFER_USAGE_VENDOR_9  = 1ULL << 53,
+    AHARDWAREBUFFER_USAGE_VENDOR_10 = 1ULL << 54,
+    AHARDWAREBUFFER_USAGE_VENDOR_11 = 1ULL << 55,
+    AHARDWAREBUFFER_USAGE_VENDOR_12 = 1ULL << 56,
+    AHARDWAREBUFFER_USAGE_VENDOR_13 = 1ULL << 57,
+    AHARDWAREBUFFER_USAGE_VENDOR_14 = 1ULL << 58,
+    AHARDWAREBUFFER_USAGE_VENDOR_15 = 1ULL << 59,
+    AHARDWAREBUFFER_USAGE_VENDOR_16 = 1ULL << 60,
+    AHARDWAREBUFFER_USAGE_VENDOR_17 = 1ULL << 61,
+    AHARDWAREBUFFER_USAGE_VENDOR_18 = 1ULL << 62,
+    AHARDWAREBUFFER_USAGE_VENDOR_19 = 1ULL << 63,
+};
+
+typedef struct AHardwareBuffer_Desc {
+    uint32_t    width;      // width in pixels
+    uint32_t    height;     // height in pixels
+    uint32_t    layers;     // number of images
+    uint32_t    format;     // One of AHARDWAREBUFFER_FORMAT_*
+    uint64_t    usage;      // Combination of AHARDWAREBUFFER_USAGE_*
+    uint32_t    stride;     // Stride in pixels, ignored for AHardwareBuffer_allocate()
+    uint32_t    rfu0;       // Initialize to zero, reserved for future use
+    uint64_t    rfu1;       // Initialize to zero, reserved for future use
+} AHardwareBuffer_Desc;
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+/**
+ * Allocates a buffer that backs an AHardwareBuffer using the passed
+ * AHardwareBuffer_Desc.
+ *
+ * Returns NO_ERROR on success, or an error number of the allocation fails for
+ * any reason.
+ */
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+        AHardwareBuffer** outBuffer);
+/**
+ * Acquire a reference on the given AHardwareBuffer object.  This prevents the
+ * object from being deleted until the last reference is removed.
+ */
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer);
+
+/**
+ * Remove a reference that was previously acquired with
+ * AHardwareBuffer_acquire().
+ */
+void AHardwareBuffer_release(AHardwareBuffer* buffer);
+
+/**
+ * Return a description of the AHardwareBuffer in the passed
+ * AHardwareBuffer_Desc struct.
+ */
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+        AHardwareBuffer_Desc* outDesc);
+
+/*
+ * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
+ * passed.  This call may block if the hardware needs to finish rendering or if
+ * CPU caches need to be synchronized, or possibly for other implementation-
+ * specific reasons.  If fence is not negative, then it specifies a fence file
+ * descriptor that will be signaled when the buffer is locked, otherwise the
+ * caller will block until the buffer is available.
+ *
+ * If rect is not NULL, the caller promises to modify only data in the area
+ * specified by rect. If rect is NULL, the caller may modify the contents of the
+ * entire buffer.
+ *
+ * The content of the buffer outside of the specified rect is NOT modified
+ * by this call.
+ *
+ * The buffer usage may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set, then
+ * outVirtualAddress is filled with the address of the buffer in virtual memory,
+ * otherwise this function will fail.
+ *
+ * THREADING CONSIDERATIONS:
+ *
+ * It is legal for several different threads to lock a buffer for read access;
+ * none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined, but
+ * will neither terminate the process nor block the caller; AHardwareBuffer_lock
+ * may return an error or leave the buffer's content into an indeterminate
+ * state.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage
+ * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
+        int32_t fence, const ARect* rect, void** outVirtualAddress);
+
+/*
+ * Unlock the AHardwareBuffer; must be called after all changes to the buffer
+ * are completed by the caller. If fence is not NULL then it will be set to a
+ * file descriptor that is signaled when all pending work on the buffer is
+ * completed. The caller is responsible for closing the fence when it is no
+ * longer needed.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence);
+
+/*
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd);
+
+/*
+ * Receive the AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
new file mode 100644
index 0000000..5290dd5
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file native_window.h
+ * @brief API for accessing a native window.
+ */
+
+#ifndef ANDROID_NATIVE_WINDOW_H
+#define ANDROID_NATIVE_WINDOW_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+#include <android/rect.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Legacy window pixel format names, kept for backwards compatibility.
+ * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
+ */
+enum {
+    // NOTE: these values must match the values from graphics/common/x.x/types.hal
+
+    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
+    WINDOW_FORMAT_RGBA_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
+    WINDOW_FORMAT_RGBX_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+    /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
+    WINDOW_FORMAT_RGB_565            = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+};
+
+/**
+ * Transforms that can be applied to buffers as they are displayed to a window.
+ *
+ * Supported transforms are any combination of horizontal mirror, vertical
+ * mirror, and clockwise 90 degree rotation, in that order. Rotations of 180
+ * and 270 degrees are made up of those basic transforms.
+ */
+enum ANativeWindowTransform {
+    ANATIVEWINDOW_TRANSFORM_IDENTITY            = 0x00,
+    ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL   = 0x01,
+    ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL     = 0x02,
+    ANATIVEWINDOW_TRANSFORM_ROTATE_90           = 0x04,
+
+    ANATIVEWINDOW_TRANSFORM_ROTATE_180          = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+                                                  ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL,
+    ANATIVEWINDOW_TRANSFORM_ROTATE_270          = ANATIVEWINDOW_TRANSFORM_ROTATE_180 |
+                                                  ANATIVEWINDOW_TRANSFORM_ROTATE_90,
+};
+
+struct ANativeWindow;
+/**
+ * Opaque type that provides access to a native window.
+ *
+ * A pointer can be obtained using {@link ANativeWindow_fromSurface()}.
+ */
+typedef struct ANativeWindow ANativeWindow;
+
+/**
+ * Struct that represents a windows buffer.
+ *
+ * A pointer can be obtained using {@link ANativeWindow_lock()}.
+ */
+typedef struct ANativeWindow_Buffer {
+    // The number of pixels that are show horizontally.
+    int32_t width;
+
+    // The number of pixels that are shown vertically.
+    int32_t height;
+
+    // The number of *pixels* that a line in the buffer takes in
+    // memory. This may be >= width.
+    int32_t stride;
+
+    // The format of the buffer. One of AHARDWAREBUFFER_FORMAT_*
+    int32_t format;
+
+    // The actual bits.
+    void* bits;
+
+    // Do not touch.
+    uint32_t reserved[6];
+} ANativeWindow_Buffer;
+
+/**
+ * Acquire a reference on the given {@link ANativeWindow} object. This prevents the object
+ * from being deleted until the reference is removed.
+ */
+void ANativeWindow_acquire(ANativeWindow* window);
+
+/**
+ * Remove a reference that was previously acquired with {@link ANativeWindow_acquire()}.
+ */
+void ANativeWindow_release(ANativeWindow* window);
+
+/**
+ * Return the current width in pixels of the window surface.
+ *
+ * \return negative value on error.
+ */
+int32_t ANativeWindow_getWidth(ANativeWindow* window);
+
+/**
+ * Return the current height in pixels of the window surface.
+ *
+ * \return a negative value on error.
+ */
+int32_t ANativeWindow_getHeight(ANativeWindow* window);
+
+/**
+ * Return the current pixel format (AHARDWAREBUFFER_FORMAT_*) of the window surface.
+ *
+ * \return a negative value on error.
+ */
+int32_t ANativeWindow_getFormat(ANativeWindow* window);
+
+/**
+ * Change the format and size of the window buffers.
+ *
+ * The width and height control the number of pixels in the buffers, not the
+ * dimensions of the window on screen. If these are different than the
+ * window's physical size, then its buffer will be scaled to match that size
+ * when compositing it to the screen. The width and height must be either both zero
+ * or both non-zero.
+ *
+ * For all of these parameters, if 0 is supplied then the window's base
+ * value will come back in force.
+ *
+ * \param width width of the buffers in pixels.
+ * \param height height of the buffers in pixels.
+ * \param format one of AHARDWAREBUFFER_FORMAT_* constants.
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
+        int32_t width, int32_t height, int32_t format);
+
+/**
+ * Lock the window's next drawing surface for writing.
+ * inOutDirtyBounds is used as an in/out parameter, upon entering the
+ * function, it contains the dirty region, that is, the region the caller
+ * intends to redraw. When the function returns, inOutDirtyBounds is updated
+ * with the actual area the caller needs to redraw -- this region is often
+ * extended by {@link ANativeWindow_lock}.
+ *
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+        ARect* inOutDirtyBounds);
+
+/**
+ * Unlock the window's drawing surface after previously locking it,
+ * posting the new buffer to the display.
+ *
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/**
+ * Set a transform that will be applied to future buffers posted to the window.
+ *
+ * \param transform combination of {@link ANativeWindowTransform} flags
+ * \return 0 for success, or -EINVAL if \p transform is invalid
+ */
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
+
+/** @} */
diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
new file mode 100644
index 0000000..71f5634
--- /dev/null
+++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+#define ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+
+/*
+ * This file contains utility functions related to AHardwareBuffer, mostly to
+ * convert to/from HAL formats.
+ *
+ * These are PRIVATE methods, so this file can NEVER appear in a public NDK
+ * header. They are used by higher level libraries such as core/jni.
+ */
+
+#include <stdint.h>
+
+struct AHardwareBuffer;
+struct ANativeWindowBuffer;
+
+namespace android {
+
+// whether this AHardwareBuffer format is valid
+bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
+
+// convert AHardwareBuffer format to HAL format (note: this is a no-op)
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
+
+// convert HAL format to AHardwareBuffer format (note: this is a no-op)
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+
+// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
+uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
+
+// convert HAL usage bits to AHardwareBuffer usage bits  (note: this is a no-op)
+uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage);
+
+class GraphicBuffer;
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer);
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer);
+
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer);
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer);
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer);
+} // namespace android
+
+#endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
new file mode 100644
index 0000000..45110c4
--- /dev/null
+++ b/libs/nativewindow/include/system/window.h
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*************************************************************************************************
+ *
+ * IMPORTANT:
+ *
+ * There is an old copy of this file in system/core/include/system/window.h, which exists only
+ * for backward source compatibility.
+ * But there are binaries out there as well, so this version of window.h must stay binary
+ * backward compatible with the one found in system/core.
+ *
+ *
+ * Source compatibility is also required for now, because this is how we're handling the
+ * transition from system/core/include (global include path) to nativewindow/include.
+ *
+ *************************************************************************************************/
+
+#pragma once
+
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+// system/window.h is a superset of the vndk
+#include <vndk/window.h>
+
+
+#ifndef __UNUSED
+#define __UNUSED __attribute__((__unused__))
+#endif
+#ifndef __deprecated
+#define __deprecated __attribute__((__deprecated__))
+#endif
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#define ANDROID_NATIVE_WINDOW_MAGIC     ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
+
+// ---------------------------------------------------------------------------
+
+typedef const native_handle_t* buffer_handle_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_rect_t
+{
+    int32_t left;
+    int32_t top;
+    int32_t right;
+    int32_t bottom;
+} android_native_rect_t;
+
+// ---------------------------------------------------------------------------
+
+// Old typedef for backwards compatibility.
+typedef ANativeWindowBuffer_t android_native_buffer_t;
+
+// ---------------------------------------------------------------------------
+
+/* attributes queriable with query() */
+enum {
+    NATIVE_WINDOW_WIDTH     = 0,
+    NATIVE_WINDOW_HEIGHT    = 1,
+    NATIVE_WINDOW_FORMAT    = 2,
+
+    /* see ANativeWindowQuery in vndk/window.h */
+    NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS,
+
+    /* Check whether queueBuffer operations on the ANativeWindow send the buffer
+     * to the window compositor.  The query sets the returned 'value' argument
+     * to 1 if the ANativeWindow DOES send queued buffers directly to the window
+     * compositor and 0 if the buffers do not go directly to the window
+     * compositor.
+     *
+     * This can be used to determine whether protected buffer content should be
+     * sent to the ANativeWindow.  Note, however, that a result of 1 does NOT
+     * indicate that queued buffers will be protected from applications or users
+     * capturing their contents.  If that behavior is desired then some other
+     * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
+     * conjunction with this query.
+     */
+    NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
+
+    /* Get the concrete type of a ANativeWindow.  See below for the list of
+     * possible return values.
+     *
+     * This query should not be used outside the Android framework and will
+     * likely be removed in the near future.
+     */
+    NATIVE_WINDOW_CONCRETE_TYPE = 5,
+
+
+    /*
+     * Default width and height of ANativeWindow buffers, these are the
+     * dimensions of the window buffers irrespective of the
+     * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
+     * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
+     */
+    NATIVE_WINDOW_DEFAULT_WIDTH = ANATIVEWINDOW_QUERY_DEFAULT_WIDTH,
+    NATIVE_WINDOW_DEFAULT_HEIGHT = ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT,
+
+    /* see ANativeWindowQuery in vndk/window.h */
+    NATIVE_WINDOW_TRANSFORM_HINT = ANATIVEWINDOW_QUERY_TRANSFORM_HINT,
+
+    /*
+     * Boolean that indicates whether the consumer is running more than
+     * one buffer behind the producer.
+     */
+    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
+
+    /*
+     * 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,
+
+    /**
+     * Transformation that will by applied to buffers by the hwcomposer.
+     * This must not be set or checked by producer endpoints, and will
+     * disable the transform hint set in SurfaceFlinger (see
+     * NATIVE_WINDOW_TRANSFORM_HINT).
+     *
+     * INTENDED USE:
+     * Temporary - Please do not use this.  This is intended only to be used
+     * by the camera's LEGACY mode.
+     *
+     * In situations where a SurfaceFlinger client wishes to set a transform
+     * that is not visible to the producer, and will always be applied in the
+     * hardware composer, the client can set this flag with
+     * native_window_set_buffers_sticky_transform.  This can be used to rotate
+     * and flip buffers consumed by hardware composer without actually changing
+     * the aspect ratio of the buffers produced.
+     */
+    NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+    /**
+     * The default data space for the buffers as set by the consumer.
+     * The values are defined in graphics.h.
+     */
+    NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
+
+    /* see ANativeWindowQuery in vndk/window.h */
+    NATIVE_WINDOW_BUFFER_AGE = ANATIVEWINDOW_QUERY_BUFFER_AGE,
+
+    /*
+     * Returns the duration of the last dequeueBuffer call in microseconds
+     */
+    NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
+
+    /*
+     * Returns the duration of the last queueBuffer call in microseconds
+     */
+    NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
+
+    /*
+     * Returns the number of image layers that the ANativeWindow buffer
+     * contains. By default this is 1, unless a buffer is explicitly allocated
+     * to contain multiple layers.
+     */
+    NATIVE_WINDOW_LAYER_COUNT = 16,
+
+    /*
+     * Returns 1 if the native window is valid, 0 otherwise. native window is valid
+     * if it is safe (i.e. no crash will occur) to call any method on it.
+     */
+    NATIVE_WINDOW_IS_VALID = 17,
+
+    /*
+     * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display
+     * present info, 0 if it won't.
+     */
+    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
+
+    /*
+     * The consumer end is capable of handling protected buffers, i.e. buffer
+     * with GRALLOC_USAGE_PROTECTED usage bits on.
+     */
+    NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19,
+};
+
+/* Valid operations for the (*perform)() hook.
+ *
+ * Values marked as 'deprecated' are supported, but have been superceded by
+ * other functionality.
+ *
+ * Values marked as 'private' should be considered private to the framework.
+ * HAL implementation code with access to an ANativeWindow should not use these,
+ * as it may not interact properly with the framework's use of the
+ * ANativeWindow.
+ */
+enum {
+// clang-format off
+    NATIVE_WINDOW_SET_USAGE                 =  0,
+    NATIVE_WINDOW_CONNECT                   =  1,   /* deprecated */
+    NATIVE_WINDOW_DISCONNECT                =  2,   /* deprecated */
+    NATIVE_WINDOW_SET_CROP                  =  3,   /* private */
+    NATIVE_WINDOW_SET_BUFFER_COUNT          =  4,
+    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY      =  5,   /* deprecated */
+    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM     =  6,
+    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP     =  7,
+    NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS    =  8,
+    NATIVE_WINDOW_SET_BUFFERS_FORMAT        =  9,
+    NATIVE_WINDOW_SET_SCALING_MODE          = 10,   /* private */
+    NATIVE_WINDOW_LOCK                      = 11,   /* private */
+    NATIVE_WINDOW_UNLOCK_AND_POST           = 12,   /* private */
+    NATIVE_WINDOW_API_CONNECT               = 13,   /* private */
+    NATIVE_WINDOW_API_DISCONNECT            = 14,   /* private */
+    NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
+    NATIVE_WINDOW_SET_POST_TRANSFORM_CROP   = 16,   /* private */
+    NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
+    NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
+    NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
+    NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
+    NATIVE_WINDOW_SET_SHARED_BUFFER_MODE    = 21,
+    NATIVE_WINDOW_SET_AUTO_REFRESH          = 22,
+    NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION= 23,
+    NATIVE_WINDOW_GET_NEXT_FRAME_ID         = 24,
+    NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS   = 25,
+    NATIVE_WINDOW_GET_COMPOSITOR_TIMING     = 26,
+    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS      = 27,
+    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT    = 28,
+    NATIVE_WINDOW_GET_HDR_SUPPORT           = 29,
+// clang-format on
+};
+
+/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
+enum {
+    /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
+     * OpenGL ES.
+     */
+    NATIVE_WINDOW_API_EGL = 1,
+
+    /* Buffers will be queued after being filled using the CPU
+     */
+    NATIVE_WINDOW_API_CPU = 2,
+
+    /* Buffers will be queued by Stagefright after being filled by a video
+     * decoder.  The video decoder can either be a software or hardware decoder.
+     */
+    NATIVE_WINDOW_API_MEDIA = 3,
+
+    /* Buffers will be queued by the the camera HAL.
+     */
+    NATIVE_WINDOW_API_CAMERA = 4,
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+    /* flip source image horizontally */
+    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+    /* flip source image vertically */
+    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+    /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+    /* rotate source image 180 degrees */
+    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+    /* rotate source image 270 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+    /* transforms source by the inverse transform of the screen it is displayed onto. This
+     * transform is applied last */
+    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
+};
+
+/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
+ * keep in sync with Surface.java in frameworks/base */
+enum {
+    /* the window content is not updated (frozen) until a buffer of
+     * the window size is received (enqueued)
+     */
+    NATIVE_WINDOW_SCALING_MODE_FREEZE           = 0,
+    /* the buffer is scaled in both dimensions to match the window size */
+    NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW  = 1,
+    /* the buffer is scaled uniformly such that the smaller dimension
+     * of the buffer matches the window size (cropping in the process)
+     */
+    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP       = 2,
+    /* the window is clipped to the size of the buffer's crop rectangle; pixels
+     * outside the crop rectangle are treated as if they are completely
+     * transparent.
+     */
+    NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP    = 3,
+};
+
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+    NATIVE_WINDOW_FRAMEBUFFER               = 0, /* FramebufferNativeWindow */
+    NATIVE_WINDOW_SURFACE                   = 1, /* Surface */
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called.  This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+/* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS
+ *
+ * Special timestamp value to indicate the timestamps aren't yet known or
+ * that they are invalid.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2;
+static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1;
+
+struct ANativeWindow
+{
+#ifdef __cplusplus
+    ANativeWindow()
+        : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
+    {
+        common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+        common.version = sizeof(ANativeWindow);
+        memset(common.reserved, 0, sizeof(common.reserved));
+    }
+
+    /* Implement the methods that sp<ANativeWindow> expects so that it
+       can be used to automatically refcount ANativeWindow's. */
+    void incStrong(const void* /*id*/) const {
+        common.incRef(const_cast<android_native_base_t*>(&common));
+    }
+    void decStrong(const void* /*id*/) const {
+        common.decRef(const_cast<android_native_base_t*>(&common));
+    }
+#endif
+
+    struct android_native_base_t common;
+
+    /* flags describing some attributes of this surface or its updater */
+    const uint32_t flags;
+
+    /* min swap interval supported by this updated */
+    const int   minSwapInterval;
+
+    /* max swap interval supported by this updated */
+    const int   maxSwapInterval;
+
+    /* horizontal and vertical resolution in DPI */
+    const float xdpi;
+    const float ydpi;
+
+    /* Some storage reserved for the OEM's driver. */
+    intptr_t    oem[4];
+
+    /*
+     * Set the swap interval for this surface.
+     *
+     * Returns 0 on success or -errno on error.
+     */
+    int     (*setSwapInterval)(struct ANativeWindow* window,
+                int interval);
+
+    /*
+     * Hook called by EGL to acquire a buffer. After this call, the buffer
+     * is not locked, so its content cannot be modified. This call may block if
+     * no buffers are available.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * Returns 0 on success or -errno on error.
+     *
+     * XXX: This function is deprecated.  It will continue to work for some
+     * time for binary compatibility, but the new dequeueBuffer function that
+     * outputs a fence file descriptor should be used in its place.
+     */
+    int     (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer** buffer);
+
+    /*
+     * hook called by EGL to lock a buffer. This MUST be called before modifying
+     * the content of a buffer. The buffer must have been acquired with
+     * dequeueBuffer first.
+     *
+     * Returns 0 on success or -errno on error.
+     *
+     * XXX: This function is deprecated.  It will continue to work for some
+     * time for binary compatibility, but it is essentially a no-op, and calls
+     * to it should be removed.
+     */
+    int     (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer* buffer);
+
+    /*
+     * Hook called by EGL when modifications to the render buffer are done.
+     * This unlocks and post the buffer.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * Buffers MUST be queued in the same order than they were dequeued.
+     *
+     * Returns 0 on success or -errno on error.
+     *
+     * XXX: This function is deprecated.  It will continue to work for some
+     * time for binary compatibility, but the new queueBuffer function that
+     * takes a fence file descriptor should be used in its place (pass a value
+     * of -1 for the fence file descriptor if there is no valid one to pass).
+     */
+    int     (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer* buffer);
+
+    /*
+     * hook used to retrieve information about the native window.
+     *
+     * Returns 0 on success or -errno on error.
+     */
+    int     (*query)(const struct ANativeWindow* window,
+                int what, int* value);
+
+    /*
+     * hook used to perform various operations on the surface.
+     * (*perform)() is a generic mechanism to add functionality to
+     * ANativeWindow while keeping backward binary compatibility.
+     *
+     * DO NOT CALL THIS HOOK DIRECTLY.  Instead, use the helper functions
+     * defined below.
+     *
+     * (*perform)() returns -ENOENT if the 'what' parameter is not supported
+     * by the surface's implementation.
+     *
+     * See above for a list of valid operations, such as
+     * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
+     */
+    int     (*perform)(struct ANativeWindow* window,
+                int operation, ... );
+
+    /*
+     * Hook used to cancel a buffer that has been dequeued.
+     * No synchronization is performed between dequeue() and cancel(), so
+     * either external synchronization is needed, or these functions must be
+     * called from the same thread.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * XXX: This function is deprecated.  It will continue to work for some
+     * time for binary compatibility, but the new cancelBuffer function that
+     * takes a fence file descriptor should be used in its place (pass a value
+     * of -1 for the fence file descriptor if there is no valid one to pass).
+     */
+    int     (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer* buffer);
+
+    /*
+     * Hook called by EGL to acquire a buffer. This call may block if no
+     * buffers are available.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * The libsync fence file descriptor returned in the int pointed to by the
+     * fenceFd argument will refer to the fence that must signal before the
+     * dequeued buffer may be written to.  A value of -1 indicates that the
+     * caller may access the buffer immediately without waiting on a fence.  If
+     * a valid file descriptor is returned (i.e. any value except -1) then the
+     * caller is responsible for closing the file descriptor.
+     *
+     * Returns 0 on success or -errno on error.
+     */
+    int     (*dequeueBuffer)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer** buffer, int* fenceFd);
+
+    /*
+     * Hook called by EGL when modifications to the render buffer are done.
+     * This unlocks and post the buffer.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * The fenceFd argument specifies a libsync fence file descriptor for a
+     * fence that must signal before the buffer can be accessed.  If the buffer
+     * can be accessed immediately then a value of -1 should be used.  The
+     * caller must not use the file descriptor after it is passed to
+     * queueBuffer, and the ANativeWindow implementation is responsible for
+     * closing it.
+     *
+     * Returns 0 on success or -errno on error.
+     */
+    int     (*queueBuffer)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer* buffer, int fenceFd);
+
+    /*
+     * Hook used to cancel a buffer that has been dequeued.
+     * No synchronization is performed between dequeue() and cancel(), so
+     * either external synchronization is needed, or these functions must be
+     * called from the same thread.
+     *
+     * The window holds a reference to the buffer between dequeueBuffer and
+     * either queueBuffer or cancelBuffer, so clients only need their own
+     * reference if they might use the buffer after queueing or canceling it.
+     * Holding a reference to a buffer after queueing or canceling it is only
+     * allowed if a specific buffer count has been set.
+     *
+     * The fenceFd argument specifies a libsync fence file decsriptor for a
+     * fence that must signal before the buffer can be accessed.  If the buffer
+     * can be accessed immediately then a value of -1 should be used.
+     *
+     * Note that if the client has not waited on the fence that was returned
+     * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+     * ensure that future uses of the buffer are preceded by a wait on that
+     * fence.  The caller must not use the file descriptor after it is passed
+     * to cancelBuffer, and the ANativeWindow implementation is responsible for
+     * closing it.
+     *
+     * Returns 0 on success or -errno on error.
+     */
+    int     (*cancelBuffer)(struct ANativeWindow* window,
+                struct ANativeWindowBuffer* buffer, int fenceFd);
+};
+
+ /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
+  * android_native_window_t is deprecated.
+  */
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow android_native_window_t __deprecated;
+
+/*
+ *  native_window_set_usage(..., usage)
+ *  Sets the intended usage flags for the next buffers
+ *  acquired with (*lockBuffer)() and on.
+ *  By default (if this function is never called), a usage of
+ *      GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
+ *  is assumed.
+ *  Calling this function will usually cause following buffers to be
+ *  reallocated.
+ */
+
+static inline int native_window_set_usage(
+        struct ANativeWindow* window, int usage)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_connect(
+        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_connect(
+        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+    return 0;
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_disconnect(
+        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_disconnect(
+        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+    return 0;
+}
+
+/*
+ * native_window_set_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size.  This function sets the crop in
+ * pre-transformed buffer pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+        struct ANativeWindow* window,
+        android_native_rect_t const * crop)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
+
+/*
+ * native_window_set_post_transform_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size.  This function sets the crop in
+ * post-transformed pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_post_transform_crop(
+        struct ANativeWindow* window,
+        android_native_rect_t const * crop)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
+}
+
+/*
+ * native_window_set_active_rect(..., active_rect)
+ *
+ * This function is deprecated and will be removed soon.  For now it simply
+ * sets the post-transform crop for compatibility while multi-project commits
+ * get checked.
+ */
+static inline int native_window_set_active_rect(
+        struct ANativeWindow* window,
+        android_native_rect_t const * active_rect) __deprecated;
+
+static inline int native_window_set_active_rect(
+        struct ANativeWindow* window,
+        android_native_rect_t const * active_rect)
+{
+    return native_window_set_post_transform_crop(window, active_rect);
+}
+
+/*
+ * native_window_set_buffer_count(..., count)
+ * Sets the number of buffers associated with this native window.
+ */
+static inline int native_window_set_buffer_count(
+        struct ANativeWindow* window,
+        size_t bufferCount)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
+}
+
+/*
+ * native_window_set_buffers_geometry(..., int w, int h, int format)
+ * All buffers dequeued after this call will have the dimensions and format
+ * specified.  A successful call to this function has the same effect as calling
+ * native_window_set_buffers_size and native_window_set_buffers_format.
+ *
+ * XXX: This function is deprecated.  The native_window_set_buffers_dimensions
+ * and native_window_set_buffers_format functions should be used instead.
+ */
+static inline int native_window_set_buffers_geometry(
+        struct ANativeWindow* window,
+        int w, int h, int format) __deprecated;
+
+static inline int native_window_set_buffers_geometry(
+        struct ANativeWindow* window,
+        int w, int h, int format)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+            w, h, format);
+}
+
+/*
+ * native_window_set_buffers_dimensions(..., int w, int h)
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_dimensions(
+        struct ANativeWindow* window,
+        int w, int h)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
+            w, h);
+}
+
+/*
+ * native_window_set_buffers_user_dimensions(..., int w, int h)
+ *
+ * Sets the user buffer size for the window, which overrides the
+ * window's size.  All buffers dequeued after this call will have the
+ * dimensions specified unless overridden by
+ * native_window_set_buffers_dimensions.  All buffers will have a
+ * fixed-size, independent from the native-window size. They will be
+ * scaled according to the scaling mode (see
+ * native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, the
+ * default buffer size will match the windows's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_user_dimensions(
+        struct ANativeWindow* window,
+        int w, int h)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
+            w, h);
+}
+
+/*
+ * native_window_set_buffers_format(..., int format)
+ * All buffers dequeued after this call will have the format specified.
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+static inline int native_window_set_buffers_format(
+        struct ANativeWindow* window,
+        int format)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
+}
+
+/*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images.  The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+        struct ANativeWindow* window,
+        android_dataspace_t dataSpace)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+            dataSpace);
+}
+
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+        struct ANativeWindow* window,
+        int transform)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+            transform);
+}
+
+/*
+ * native_window_set_buffers_sticky_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified applied on top of the regular buffer
+ * transform.  Setting this transform will disable the transform hint.
+ *
+ * Temporary - This is only intended to be used by the LEGACY camera mode, do
+ *   not use this for anything else.
+ */
+static inline int native_window_set_buffers_sticky_transform(
+        struct ANativeWindow* window,
+        int transform)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
+            transform);
+}
+
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+static inline int native_window_set_buffers_timestamp(
+        struct ANativeWindow* window,
+        int64_t timestamp)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+            timestamp);
+}
+
+/*
+ * native_window_set_scaling_mode(..., int mode)
+ * All buffers queued after this call will be associated with the scaling mode
+ * specified.
+ */
+static inline int native_window_set_scaling_mode(
+        struct ANativeWindow* window,
+        int mode)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
+            mode);
+}
+
+/*
+ * native_window_api_connect(..., int api)
+ * connects an API to this window. only one API can be connected at a time.
+ * Returns -EINVAL if for some reason the window cannot be connected, which
+ * can happen if it's connected to some other API.
+ */
+static inline int native_window_api_connect(
+        struct ANativeWindow* window, int api)
+{
+    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
+}
+
+/*
+ * native_window_api_disconnect(..., int api)
+ * disconnect the API from this window.
+ * An error is returned if for instance the window wasn't connected in the
+ * first place.
+ */
+static inline int native_window_api_disconnect(
+        struct ANativeWindow* window, int api)
+{
+    return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
+}
+
+/*
+ * native_window_dequeue_buffer_and_wait(...)
+ * Dequeue a buffer and wait on the fence associated with that buffer.  The
+ * buffer may safely be accessed immediately upon this function returning.  An
+ * error is returned if either of the dequeue or the wait operations fail.
+ */
+static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
+        struct ANativeWindowBuffer** anb) {
+    return anw->dequeueBuffer_DEPRECATED(anw, anb);
+}
+
+/*
+ * native_window_set_sideband_stream(..., native_handle_t*)
+ * Attach a sideband buffer stream to a native window.
+ */
+static inline int native_window_set_sideband_stream(
+        struct ANativeWindow* window,
+        native_handle_t* sidebandHandle)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
+            sidebandHandle);
+}
+
+/*
+ * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
+ * Set the surface damage (i.e., the region of the surface that has changed
+ * since the previous frame). The damage set by this call will be reset (to the
+ * default of full-surface damage) after calling queue, so this must be called
+ * prior to every frame with damage that does not cover the whole surface if the
+ * caller desires downstream consumers to use this optimization.
+ *
+ * The damage region is specified as an array of rectangles, with the important
+ * caveat that the origin of the surface is considered to be the bottom-left
+ * corner, as in OpenGL ES.
+ *
+ * If numRects is set to 0, rects may be NULL, and the surface damage will be
+ * set to the full surface (the same as if this function had not been called for
+ * this frame).
+ */
+static inline int native_window_set_surface_damage(
+        struct ANativeWindow* window,
+        const android_native_rect_t* rects, size_t numRects)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
+            rects, numRects);
+}
+
+/*
+ * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
+ * Enable/disable shared buffer mode
+ */
+static inline int native_window_set_shared_buffer_mode(
+        struct ANativeWindow* window,
+        bool sharedBufferMode)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
+            sharedBufferMode);
+}
+
+/*
+ * native_window_set_auto_refresh(..., autoRefresh)
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+static inline int native_window_set_auto_refresh(
+        struct ANativeWindow* window,
+        bool autoRefresh)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
+}
+
+static inline int native_window_get_refresh_cycle_duration(
+        struct ANativeWindow* window,
+        int64_t* outRefreshDuration)
+{
+    return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION,
+            outRefreshDuration);
+}
+
+static inline int native_window_get_next_frame_id(
+        struct ANativeWindow* window, uint64_t* frameId)
+{
+    return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId);
+}
+
+static inline int native_window_enable_frame_timestamps(
+        struct ANativeWindow* window, bool enable)
+{
+    return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS,
+            enable);
+}
+
+static inline int native_window_get_compositor_timing(
+        struct ANativeWindow* window,
+        int64_t* compositeDeadline, int64_t* compositeInterval,
+        int64_t* compositeToPresentLatency)
+{
+    return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING,
+            compositeDeadline, compositeInterval, compositeToPresentLatency);
+}
+
+static inline int native_window_get_frame_timestamps(
+        struct ANativeWindow* window, uint64_t frameId,
+        int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
+        int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
+        int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
+        int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
+        int64_t* outReleaseTime)
+{
+    return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
+            frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
+            outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGpuCompositionDoneTime, outDisplayPresentTime,
+            outDequeueReadyTime, outReleaseTime);
+}
+
+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);
+}
+
+static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+                                                bool* outSupport) {
+  return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+}
+
+__END_DECLS
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
new file mode 100644
index 0000000..802edcc
--- /dev/null
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H
+#define ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H
+
+// vndk is a superset of the NDK
+#include <android/hardware_buffer.h>
+
+#include <cutils/native_handle.h>
+
+__BEGIN_DECLS
+
+const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer);
+
+
+/**
+ * Buffer pixel formats.
+ */
+enum {
+    /* for future proofing, keep these in sync with system/graphics-base.h */
+
+    /* same as HAL_PIXEL_FORMAT_BGRA_8888 */
+    AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM           = 5,
+    /* same as HAL_PIXEL_FORMAT_YV12 */
+    AHARDWAREBUFFER_FORMAT_YV12                     = 0x32315659,
+    /* same as HAL_PIXEL_FORMAT_Y8 */
+    AHARDWAREBUFFER_FORMAT_Y8                       = 0x20203859,
+    /* same as HAL_PIXEL_FORMAT_Y16 */
+    AHARDWAREBUFFER_FORMAT_Y16                      = 0x20363159,
+    /* same as HAL_PIXEL_FORMAT_RAW16 */
+    AHARDWAREBUFFER_FORMAT_RAW16                    = 0x20,
+    /* same as HAL_PIXEL_FORMAT_RAW10 */
+    AHARDWAREBUFFER_FORMAT_RAW10                    = 0x25,
+    /* same as HAL_PIXEL_FORMAT_RAW12 */
+    AHARDWAREBUFFER_FORMAT_RAW12                    = 0x26,
+    /* same as HAL_PIXEL_FORMAT_RAW_OPAQUE */
+    AHARDWAREBUFFER_FORMAT_RAW_OPAQUE               = 0x24,
+    /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */
+    AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED   = 0x22,
+    /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */
+    AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420             = 0x23,
+    /* same as HAL_PIXEL_FORMAT_YCBCR_422_888 */
+    AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422             = 0x27,
+    /* same as HAL_PIXEL_FORMAT_YCBCR_444_888 */
+    AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444             = 0x28,
+    /* same as HAL_PIXEL_FORMAT_FLEX_RGB_888 */
+    AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8              = 0x29,
+    /* same as HAL_PIXEL_FORMAT_FLEX_RGBA_8888 */
+    AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8            = 0x2A,
+    /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */
+    AHARDWAREBUFFER_FORMAT_YCbCr_422_SP             = 0x10,
+    /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
+    AHARDWAREBUFFER_FORMAT_YCrCb_420_SP             = 0x11,
+    /* same as HAL_PIXEL_FORMAT_YCBCR_422_I */
+    AHARDWAREBUFFER_FORMAT_YCbCr_422_I              = 0x14,
+};
+
+__END_DECLS
+
+#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h
new file mode 100644
index 0000000..95618c4
--- /dev/null
+++ b/libs/nativewindow/include/vndk/window.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H
+#define ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <system/graphics-base.h>
+#include <cutils/native_handle.h>
+
+// vndk is a superset of the NDK
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
+#else
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
+#endif
+
+#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d)  \
+    ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
+     (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
+     (ANDROID_NATIVE_UNSIGNED_CAST(c) <<  8) | \
+     (ANDROID_NATIVE_UNSIGNED_CAST(d)))
+
+#define ANDROID_NATIVE_BUFFER_MAGIC     ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
+
+
+/*****************************************************************************/
+
+typedef struct android_native_base_t
+{
+    /* a magic value defined by the actual EGL native type */
+    int magic;
+
+    /* the sizeof() of the actual EGL native type */
+    int version;
+
+    void* reserved[4];
+
+    /* reference-counting interface */
+    void (*incRef)(struct android_native_base_t* base);
+    void (*decRef)(struct android_native_base_t* base);
+} android_native_base_t;
+
+typedef struct ANativeWindowBuffer
+{
+#ifdef __cplusplus
+    ANativeWindowBuffer() {
+        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+        common.version = sizeof(ANativeWindowBuffer);
+        memset(common.reserved, 0, sizeof(common.reserved));
+    }
+
+    // Implement the methods that sp<ANativeWindowBuffer> expects so that it
+    // can be used to automatically refcount ANativeWindowBuffer's.
+    void incStrong(const void* /*id*/) const {
+        common.incRef(const_cast<android_native_base_t*>(&common));
+    }
+    void decStrong(const void* /*id*/) const {
+        common.decRef(const_cast<android_native_base_t*>(&common));
+    }
+#endif
+
+    struct android_native_base_t common;
+
+    int width;
+    int height;
+    int stride;
+    int format;
+    int usage;
+    uintptr_t layerCount;
+
+    void* reserved[1];
+
+    const native_handle_t* handle;
+
+    void* reserved_proc[8];
+} ANativeWindowBuffer_t;
+
+typedef struct ANativeWindowBuffer ANativeWindowBuffer;
+
+/*
+ * Convert this ANativeWindowBuffer into a AHardwareBuffer
+ */
+AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb);
+
+/*****************************************************************************/
+
+/*
+ * Stores a value into one of the 4 available slots
+ * Retrieve the value with ANativeWindow_OemStorageGet()
+ *
+ * slot: 0 to 3
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value);
+
+
+/*
+ * Retrieves a value from one of the 4 available slots
+ * By default the returned value is 0 if it wasn't set by ANativeWindow_OemStorageSet()
+ *
+ * slot: 0 to 3
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value);
+
+
+/*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval);
+
+
+/*
+ * queries that can be used with ANativeWindow_query() and ANativeWindow_queryf()
+ */
+enum ANativeWindowQuery {
+    /* The minimum number of buffers that must remain un-dequeued after a buffer
+     * has been queued.  This value applies only if set_buffer_count was used to
+     * override the number of buffers and if a buffer has since been queued.
+     * Users of the set_buffer_count ANativeWindow method should query this
+     * value before calling set_buffer_count.  If it is necessary to have N
+     * buffers simultaneously dequeued as part of the steady-state operation,
+     * and this query returns M then N+M buffers should be requested via
+     * native_window_set_buffer_count.
+     *
+     * Note that this value does NOT apply until a single buffer has been
+     * queued.  In particular this means that it is possible to:
+     *
+     * 1. Query M = min undequeued buffers
+     * 2. Set the buffer count to N + M
+     * 3. Dequeue all N + M buffers
+     * 4. Cancel M buffers
+     * 5. Queue, dequeue, queue, dequeue, ad infinitum
+     */
+    ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS = 3,
+
+    /*
+     * Default width of ANativeWindow buffers, these are the
+     * dimensions of the window buffers irrespective of the
+     * ANativeWindow_setBuffersDimensions() call and match the native window
+     * size.
+     */
+    ANATIVEWINDOW_QUERY_DEFAULT_WIDTH = 6,
+    ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT = 7,
+
+    /*
+     * transformation that will most-likely be applied to buffers. This is only
+     * a hint, the actual transformation applied might be different.
+     *
+     * INTENDED USE:
+     *
+     * The transform hint can be used by a producer, for instance the GLES
+     * driver, to pre-rotate the rendering such that the final transformation
+     * in the composer is identity. This can be very useful when used in
+     * conjunction with the h/w composer HAL, in situations where it
+     * cannot handle arbitrary rotations.
+     *
+     * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
+     *    queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
+     *
+     * 2. The GL driver overrides the width and height of the ANW to
+     *    account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
+     *    NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
+     *    according to NATIVE_WINDOW_TRANSFORM_HINT and calling
+     *    native_window_set_buffers_dimensions().
+     *
+     * 3. The GL driver dequeues a buffer of the new pre-rotated size.
+     *
+     * 4. The GL driver renders to the buffer such that the image is
+     *    already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
+     *    to the rendering.
+     *
+     * 5. The GL driver calls native_window_set_transform to apply
+     *    inverse transformation to the buffer it just rendered.
+     *    In order to do this, the GL driver needs
+     *    to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
+     *    done easily:
+     *
+     *        int hintTransform, inverseTransform;
+     *        query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
+     *        inverseTransform = hintTransform;
+     *        if (hintTransform & HAL_TRANSFORM_ROT_90)
+     *            inverseTransform ^= HAL_TRANSFORM_ROT_180;
+     *
+     *
+     * 6. The GL driver queues the pre-transformed buffer.
+     *
+     * 7. The composer combines the buffer transform with the display
+     *    transform.  If the buffer transform happens to cancel out the
+     *    display transform then no rotation is needed.
+     *
+     */
+    ANATIVEWINDOW_QUERY_TRANSFORM_HINT = 8,
+
+    /*
+     * Returns the age of the contents of the most recently dequeued buffer as
+     * the number of frames that have elapsed since it was last queued. For
+     * example, if the window is double-buffered, the age of any given buffer in
+     * steady state will be 2. If the dequeued buffer has never been queued, its
+     * age will be 0.
+     */
+    ANATIVEWINDOW_QUERY_BUFFER_AGE = 13,
+
+    /* min swap interval supported by this compositor */
+    ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL = 0x10000,
+
+    /* max swap interval supported by this compositor */
+    ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL = 0x10001,
+
+    /* horizontal resolution in DPI. value is float, use queryf() */
+    ANATIVEWINDOW_QUERY_XDPI = 0x10002,
+
+    /* vertical resolution in DPI. value is float, use queryf() */
+    ANATIVEWINDOW_QUERY_YDPI = 0x10003,
+};
+
+typedef enum ANativeWindowQuery ANativeWindowQuery;
+
+/*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery query, int* value);
+int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery query, float* value);
+
+
+/*
+ * Hook called by EGL to acquire a buffer. This call may block if no
+ * buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The libsync fence file descriptor returned in the int pointed to by the
+ * fenceFd argument will refer to the fence that must signal before the
+ * dequeued buffer may be written to.  A value of -1 indicates that the
+ * caller may access the buffer immediately without waiting on a fence.  If
+ * a valid file descriptor is returned (i.e. any value except -1) then the
+ * caller is responsible for closing the file descriptor.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd);
+
+
+/*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file descriptor for a
+ * fence that must signal before the buffer can be accessed.  If the buffer
+ * can be accessed immediately then a value of -1 should be used.  The
+ * caller must not use the file descriptor after it is passed to
+ * queueBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+
+/*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file decsriptor for a
+ * fence that must signal before the buffer can be accessed.  If the buffer
+ * can be accessed immediately then a value of -1 should be used.
+ *
+ * Note that if the client has not waited on the fence that was returned
+ * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+ * ensure that future uses of the buffer are preceded by a wait on that
+ * fence.  The caller must not use the file descriptor after it is passed
+ * to cancelBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+/*
+ *  Sets the intended usage flags for the next buffers.
+ *
+ *  usage: one of AHARDWAREBUFFER_USAGE_* constant
+ *
+ *  By default (if this function is never called), a usage of
+ *      AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT
+ *  is assumed.
+ *
+ *  Calling this function will usually cause following buffers to be
+ *  reallocated.
+ */
+int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage);
+
+
+/*
+ * Sets the number of buffers associated with this native window.
+ */
+int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount);
+
+
+/*
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h);
+
+
+/*
+ * All buffers dequeued after this call will have the format specified.
+ * format: one of AHARDWAREBUFFER_FORMAT_* constant
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format);
+
+
+/*
+ * All buffers queued after this call will be associated with the timestamp in nanosecond
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp);
+
+
+/*
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images.  The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace);
+
+
+/*
+ * Enable/disable shared buffer mode
+ */
+int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode);
+
+
+/*
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh);
+
+
+/*****************************************************************************/
+
+__END_DECLS
+
+#endif /* ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
new file mode 100644
index 0000000..b1d1a72
--- /dev/null
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -0,0 +1,26 @@
+LIBNATIVEWINDOW {
+  global:
+    AHardwareBuffer_acquire;
+    AHardwareBuffer_allocate;
+    AHardwareBuffer_describe;
+    AHardwareBuffer_fromHardwareBuffer;
+    AHardwareBuffer_lock;
+    AHardwareBuffer_recvHandleFromUnixSocket;
+    AHardwareBuffer_release;
+    AHardwareBuffer_sendHandleToUnixSocket;
+    AHardwareBuffer_toHardwareBuffer;
+    AHardwareBuffer_unlock;
+    ANativeWindow_acquire;
+    ANativeWindow_fromSurface;
+    ANativeWindow_fromSurfaceTexture;
+    ANativeWindow_getFormat;
+    ANativeWindow_getHeight;
+    ANativeWindow_getWidth;
+    ANativeWindow_lock;
+    ANativeWindow_release;
+    ANativeWindow_setBuffersGeometry;
+    ANativeWindow_setBuffersTransform;
+    ANativeWindow_unlockAndPost;
+  local:
+    *;
+};
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
new file mode 100644
index 0000000..cc2731d
--- /dev/null
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer_test"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using android::hardware::graphics::common::V1_0::BufferUsage;
+
+static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
+        uint64_t actual, const char* type) {
+    std::ostringstream ss;
+    ss << type << " 0x" << std::hex << actual
+            << " does not match expected " << type << " 0x" << std::hex
+            << expected;
+    return ::testing::AssertionFailure() << ss.str();
+}
+
+static ::testing::AssertionResult TestUsageConversion(
+        uint64_t grallocUsage, uint64_t hardwareBufferUsage) {
+    uint64_t convertedGrallocUsage = AHardwareBuffer_convertToGrallocUsageBits(hardwareBufferUsage);
+    if (convertedGrallocUsage != grallocUsage)
+        return BuildHexFailureMessage(grallocUsage, convertedGrallocUsage, "converToGralloc");
+
+    uint64_t convertedHArdwareBufferUsage = AHardwareBuffer_convertFromGrallocUsageBits(grallocUsage);
+    if (convertedHArdwareBufferUsage != grallocUsage)
+        return BuildHexFailureMessage(grallocUsage, convertedHArdwareBufferUsage, "convertFromGralloc");
+
+    return testing::AssertionSuccess();
+}
+
+// This is a unit test rather than going through AHardwareBuffer because not
+// all flags may be supported by the host device.
+TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) {
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_RARELY,
+            AHARDWAREBUFFER_USAGE_CPU_READ_RARELY));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_OFTEN,
+            AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_RARELY,
+            AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_OFTEN,
+            AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_TEXTURE,
+            AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_RENDER_TARGET,
+            AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_DATA_BUFFER,
+            AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::PROTECTED,
+            AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::SENSOR_DIRECT_DATA,
+            AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA));
+    EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::VIDEO_ENCODER,
+            AHARDWAREBUFFER_USAGE_VIDEO_ENCODE));
+
+    EXPECT_TRUE(TestUsageConversion(1ull<<28, AHARDWAREBUFFER_USAGE_VENDOR_0));
+    EXPECT_TRUE(TestUsageConversion(1ull<<29, AHARDWAREBUFFER_USAGE_VENDOR_1));
+    EXPECT_TRUE(TestUsageConversion(1ull<<30, AHARDWAREBUFFER_USAGE_VENDOR_2));
+    EXPECT_TRUE(TestUsageConversion(1ull<<31, AHARDWAREBUFFER_USAGE_VENDOR_3));
+    EXPECT_TRUE(TestUsageConversion(1ull<<48, AHARDWAREBUFFER_USAGE_VENDOR_4));
+    EXPECT_TRUE(TestUsageConversion(1ull<<49, AHARDWAREBUFFER_USAGE_VENDOR_5));
+    EXPECT_TRUE(TestUsageConversion(1ull<<50, AHARDWAREBUFFER_USAGE_VENDOR_6));
+    EXPECT_TRUE(TestUsageConversion(1ull<<51, AHARDWAREBUFFER_USAGE_VENDOR_7));
+    EXPECT_TRUE(TestUsageConversion(1ull<<52, AHARDWAREBUFFER_USAGE_VENDOR_8));
+    EXPECT_TRUE(TestUsageConversion(1ull<<53, AHARDWAREBUFFER_USAGE_VENDOR_9));
+    EXPECT_TRUE(TestUsageConversion(1ull<<54, AHARDWAREBUFFER_USAGE_VENDOR_10));
+    EXPECT_TRUE(TestUsageConversion(1ull<<55, AHARDWAREBUFFER_USAGE_VENDOR_11));
+    EXPECT_TRUE(TestUsageConversion(1ull<<56, AHARDWAREBUFFER_USAGE_VENDOR_12));
+    EXPECT_TRUE(TestUsageConversion(1ull<<57, AHARDWAREBUFFER_USAGE_VENDOR_13));
+    EXPECT_TRUE(TestUsageConversion(1ull<<58, AHARDWAREBUFFER_USAGE_VENDOR_14));
+    EXPECT_TRUE(TestUsageConversion(1ull<<59, AHARDWAREBUFFER_USAGE_VENDOR_15));
+    EXPECT_TRUE(TestUsageConversion(1ull<<60, AHARDWAREBUFFER_USAGE_VENDOR_16));
+    EXPECT_TRUE(TestUsageConversion(1ull<<61, AHARDWAREBUFFER_USAGE_VENDOR_17));
+    EXPECT_TRUE(TestUsageConversion(1ull<<62, AHARDWAREBUFFER_USAGE_VENDOR_18));
+    EXPECT_TRUE(TestUsageConversion(1ull<<63, AHARDWAREBUFFER_USAGE_VENDOR_19));
+
+    // Test some more complex flag combinations.
+    EXPECT_TRUE(TestUsageConversion(
+            (uint64_t)BufferUsage::CPU_READ_RARELY |
+            (uint64_t)BufferUsage::CPU_WRITE_RARELY,
+            AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
+
+EXPECT_TRUE(TestUsageConversion(
+        (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE |
+            1ull << 29 | 1ull << 57,
+        AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+        AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13));
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
new file mode 100644
index 0000000..b89c35a
--- /dev/null
+++ b/libs/nativewindow/tests/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "AHardwareBufferTest",
+    shared_libs: [
+        "libnativewindow",
+        "android.hardware.graphics.common@1.0",
+    ],
+    srcs: [
+        "AHardwareBufferTest.cpp",
+        "c_compatibility.c"],
+}
diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c
new file mode 100644
index 0000000..befd88f
--- /dev/null
+++ b/libs/nativewindow/tests/c_compatibility.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware_buffer.h>
+#include <android/native_window.h>
+#include <vndk/hardware_buffer.h>
+#include <vndk/window.h>
+
+// this checks that all these headers are C-compatible
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
new file mode 100644
index 0000000..171a627
--- /dev/null
+++ b/libs/sensor/Android.bp
@@ -0,0 +1,61 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libsensor",
+
+    clang: true,
+    cppflags: [
+        "-Weverything",
+        "-Werror",
+
+        // The static constructors and destructors in this library have not been noted to
+        // introduce significant overheads
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+
+        // We only care about compiling as C++14
+        "-Wno-c++98-compat-pedantic",
+
+        // android/sensors.h uses nested anonymous unions and anonymous structs
+        "-Wno-nested-anon-types",
+        "-Wno-gnu-anonymous-struct",
+
+        // Don't warn about struct padding
+        "-Wno-padded",
+    ],
+
+    srcs: [
+        "BitTube.cpp",
+        "ISensorEventConnection.cpp",
+        "ISensorServer.cpp",
+        "Sensor.cpp",
+        "SensorEventQueue.cpp",
+        "SensorManager.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+        "liblog",
+        "libhardware",
+    ],
+
+    export_include_dirs: ["include"],
+
+    export_shared_lib_headers: ["libbinder", "libhardware"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/sensor/BitTube.cpp b/libs/sensor/BitTube.cpp
new file mode 100644
index 0000000..93555c8
--- /dev/null
+++ b/libs/sensor/BitTube.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/BitTube.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+// Socket buffer size.  The default is typically about 128KB, which is much larger than
+// we really need.  So we make it smaller.
+static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
+
+
+BitTube::BitTube()
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
+}
+
+BitTube::BitTube(size_t bufsize)
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    init(bufsize, bufsize);
+}
+
+BitTube::BitTube(const Parcel& data)
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    mReceiveFd = dup(data.readFileDescriptor());
+    if (mReceiveFd < 0) {
+        mReceiveFd = -errno;
+        ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
+                strerror(-mReceiveFd));
+    }
+}
+
+BitTube::~BitTube()
+{
+    if (mSendFd >= 0)
+        close(mSendFd);
+
+    if (mReceiveFd >= 0)
+        close(mReceiveFd);
+}
+
+void BitTube::init(size_t rcvbuf, size_t sndbuf) {
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
+        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
+        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+        // sine we don't use the "return channel", we keep it small...
+        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
+        mReceiveFd = sockets[0];
+        mSendFd = sockets[1];
+    } else {
+        mReceiveFd = -errno;
+        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+    }
+}
+
+status_t BitTube::initCheck() const
+{
+    if (mReceiveFd < 0) {
+        return status_t(mReceiveFd);
+    }
+    return NO_ERROR;
+}
+
+int BitTube::getFd() const
+{
+    return mReceiveFd;
+}
+
+int BitTube::getSendFd() const
+{
+    return mSendFd;
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
+        // cannot return less than size, since we're using SOCK_SEQPACKET
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    return err == 0 ? len : -err;
+}
+
+ssize_t BitTube::read(void* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    if (err == EAGAIN || err == EWOULDBLOCK) {
+        // EAGAIN means that we have non-blocking I/O but there was
+        // no data to be read. Nothing the client should care about.
+        return 0;
+    }
+    return err == 0 ? len : -err;
+}
+
+status_t BitTube::writeToParcel(Parcel* reply) const
+{
+    if (mReceiveFd < 0)
+        return -EINVAL;
+
+    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+    close(mReceiveFd);
+    mReceiveFd = -1;
+    return result;
+}
+
+
+ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
+        void const* events, size_t count, size_t objSize)
+{
+    const char* vaddr = reinterpret_cast<const char*>(events);
+    ssize_t size = tube->write(vaddr, count*objSize);
+
+    // should never happen because of SOCK_SEQPACKET
+    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+            "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
+            count, objSize, size);
+
+    //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
+        void* events, size_t count, size_t objSize)
+{
+    char* vaddr = reinterpret_cast<char*>(events);
+    ssize_t size = tube->read(vaddr, count*objSize);
+
+    // should never happen because of SOCK_SEQPACKET
+    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+            "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
+            count, objSize, size);
+
+    //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/ISensorEventConnection.cpp b/libs/sensor/ISensorEventConnection.cpp
new file mode 100644
index 0000000..8a3a623
--- /dev/null
+++ b/libs/sensor/ISensorEventConnection.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/ISensorEventConnection.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <sensor/BitTube.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+    GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+    ENABLE_DISABLE,
+    SET_EVENT_RATE,
+    FLUSH_SENSOR,
+    CONFIGURE_CHANNEL
+};
+
+class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
+{
+public:
+    explicit BpSensorEventConnection(const sp<IBinder>& impl)
+        : BpInterface<ISensorEventConnection>(impl)
+    {
+    }
+
+    virtual ~BpSensorEventConnection();
+
+    virtual sp<BitTube> getSensorChannel() const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
+        return new BitTube(reply);
+    }
+
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt32(enabled);
+        data.writeInt64(samplingPeriodNs);
+        data.writeInt64(maxBatchReportLatencyNs);
+        data.writeInt32(reservedFlags);
+        remote()->transact(ENABLE_DISABLE, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t setEventRate(int handle, nsecs_t ns)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt64(ns);
+        remote()->transact(SET_EVENT_RATE, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t flush() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        remote()->transact(FLUSH_SENSOR, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt32(rateLevel);
+        remote()->transact(CONFIGURE_CHANNEL, data, &reply);
+        return reply.readInt32();
+    }
+};
+
+// Out-of-line virtual method definition to trigger vtable emission in this
+// translation unit (see clang warning -Wweak-vtables)
+BpSensorEventConnection::~BpSensorEventConnection() {}
+
+IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
+
+// ----------------------------------------------------------------------------
+
+status_t BnSensorEventConnection::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case GET_SENSOR_CHANNEL: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            sp<BitTube> channel(getSensorChannel());
+            channel->writeToParcel(reply);
+            return NO_ERROR;
+        }
+        case ENABLE_DISABLE: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            int enabled = data.readInt32();
+            nsecs_t samplingPeriodNs = data.readInt64();
+            nsecs_t maxBatchReportLatencyNs = data.readInt64();
+            int reservedFlags = data.readInt32();
+            status_t result = enableDisable(handle, enabled, samplingPeriodNs,
+                                            maxBatchReportLatencyNs, reservedFlags);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case SET_EVENT_RATE: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            nsecs_t ns = data.readInt64();
+            status_t result = setEventRate(handle, ns);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case FLUSH_SENSOR: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            status_t result = flush();
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case CONFIGURE_CHANNEL: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            int rateLevel = data.readInt32();
+            status_t result = configureChannel(handle, rateLevel);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
new file mode 100644
index 0000000..74186df
--- /dev/null
+++ b/libs/sensor/ISensorServer.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/ISensorServer.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/ISensorEventConnection.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+    GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
+    CREATE_SENSOR_EVENT_CONNECTION,
+    ENABLE_DATA_INJECTION,
+    GET_DYNAMIC_SENSOR_LIST,
+    CREATE_SENSOR_DIRECT_CONNECTION,
+    SET_OPERATION_PARAMETER,
+};
+
+class BpSensorServer : public BpInterface<ISensorServer>
+{
+public:
+    explicit BpSensorServer(const sp<IBinder>& impl)
+        : BpInterface<ISensorServer>(impl)
+    {
+    }
+
+    virtual ~BpSensorServer();
+
+    virtual Vector<Sensor> getSensorList(const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        remote()->transact(GET_SENSOR_LIST, data, &reply);
+        Sensor s;
+        Vector<Sensor> v;
+        uint32_t n = reply.readUint32();
+        v.setCapacity(n);
+        while (n--) {
+            reply.read(s);
+            v.add(s);
+        }
+        return v;
+    }
+
+    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply);
+        Sensor s;
+        Vector<Sensor> v;
+        uint32_t n = reply.readUint32();
+        v.setCapacity(n);
+        while (n--) {
+            reply.read(s);
+            v.add(s);
+        }
+        return v;
+    }
+
+    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
+             int mode, const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString8(packageName);
+        data.writeInt32(mode);
+        data.writeString16(opPackageName);
+        remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
+        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+    }
+
+    virtual int isDataInjectionEnabled() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        data.writeUint32(size);
+        data.writeInt32(type);
+        data.writeInt32(format);
+        data.writeNativeHandle(resource);
+        remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply);
+        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+    }
+
+    virtual int setOperationParameter(
+            int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeInt32(type);
+        data.writeUint32(static_cast<uint32_t>(floats.size()));
+        for (auto i : floats) {
+            data.writeFloat(i);
+        }
+        data.writeUint32(static_cast<uint32_t>(ints.size()));
+        for (auto i : ints) {
+            data.writeInt32(i);
+        }
+        remote()->transact(SET_OPERATION_PARAMETER, data, &reply);
+        return reply.readInt32();
+    }
+};
+
+// Out-of-line virtual method definition to trigger vtable emission in this
+// translation unit (see clang warning -Wweak-vtables)
+BpSensorServer::~BpSensorServer() {}
+
+IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
+
+// ----------------------------------------------------------------------
+
+status_t BnSensorServer::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case GET_SENSOR_LIST: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            Vector<Sensor> v(getSensorList(opPackageName));
+            size_t n = v.size();
+            reply->writeUint32(static_cast<uint32_t>(n));
+            for (size_t i = 0; i < n; i++) {
+                reply->write(v[i]);
+            }
+            return NO_ERROR;
+        }
+        case CREATE_SENSOR_EVENT_CONNECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            String8 packageName = data.readString8();
+            int32_t mode = data.readInt32();
+            const String16& opPackageName = data.readString16();
+            sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
+                    opPackageName));
+            reply->writeStrongBinder(IInterface::asBinder(connection));
+            return NO_ERROR;
+        }
+        case ENABLE_DATA_INJECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            int32_t ret = isDataInjectionEnabled();
+            reply->writeInt32(static_cast<int32_t>(ret));
+            return NO_ERROR;
+        }
+        case GET_DYNAMIC_SENSOR_LIST: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            Vector<Sensor> v(getDynamicSensorList(opPackageName));
+            size_t n = v.size();
+            reply->writeUint32(static_cast<uint32_t>(n));
+            for (size_t i = 0; i < n; i++) {
+                reply->write(v[i]);
+            }
+            return NO_ERROR;
+        }
+        case CREATE_SENSOR_DIRECT_CONNECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            uint32_t size = data.readUint32();
+            int32_t type = data.readInt32();
+            int32_t format = data.readInt32();
+            native_handle_t *resource = data.readNativeHandle();
+            sp<ISensorEventConnection> ch =
+                    createSensorDirectConnection(opPackageName, size, type, format, resource);
+            native_handle_close(resource);
+            native_handle_delete(resource);
+            reply->writeStrongBinder(IInterface::asBinder(ch));
+            return NO_ERROR;
+        }
+        case SET_OPERATION_PARAMETER: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            int32_t type;
+            Vector<float> floats;
+            Vector<int32_t> ints;
+
+            type = data.readInt32();
+            floats.resize(data.readUint32());
+            for (auto &i : floats) {
+                i = data.readFloat();
+            }
+            ints.resize(data.readUint32());
+            for (auto &i : ints) {
+                i = data.readInt32();
+            }
+
+            int32_t ret = setOperationParameter(type, floats, ints);
+            reply->writeInt32(ret);
+            return NO_ERROR;
+        }
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
new file mode 100644
index 0000000..c2d477e
--- /dev/null
+++ b/libs/sensor/Sensor.cpp
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/Sensor.h>
+
+#include <inttypes.h>
+
+#include <binder/AppOpsManager.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+Sensor::Sensor(const char * name) :
+        mName(name), mHandle(0), mType(0),
+        mMinValue(0), mMaxValue(0), mResolution(0),
+        mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
+        mFifoMaxEventCount(0), mRequiredAppOp(0),
+        mMaxDelay(0), mFlags(0) {
+}
+
+Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) :
+        Sensor(*hwSensor, uuid_t(), halVersion) {
+}
+
+Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) {
+    mName = hwSensor.name;
+    mVendor = hwSensor.vendor;
+    mVersion = hwSensor.version;
+    mHandle = hwSensor.handle;
+    mType = hwSensor.type;
+    mMinValue = 0;                      // FIXME: minValue
+    mMaxValue = hwSensor.maxRange;      // FIXME: maxValue
+    mResolution = hwSensor.resolution;
+    mPower = hwSensor.power;
+    mMinDelay = hwSensor.minDelay;
+    mFlags = 0;
+    mUuid = uuid;
+
+    // Set fifo event count zero for older devices which do not support batching. Fused
+    // sensors also have their fifo counts set to zero.
+    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
+        mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
+        mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
+    } else {
+        mFifoReservedEventCount = 0;
+        mFifoMaxEventCount = 0;
+    }
+
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        if (hwSensor.maxDelay > INT_MAX) {
+            // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
+            // always fit in a 32 bit integer, log error and cap it to INT_MAX.
+            ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
+                  static_cast<int64_t>(hwSensor.maxDelay));
+            mMaxDelay = INT_MAX;
+        } else {
+            mMaxDelay = static_cast<int32_t>(hwSensor.maxDelay);
+        }
+    } else {
+        // For older hals set maxDelay to 0.
+        mMaxDelay = 0;
+    }
+
+    // Ensure existing sensors have correct string type, required permissions and reporting mode.
+    // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity
+    // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older
+    // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors.
+    // All the OEM defined defined sensors have flags set to whatever is provided by the HAL.
+    switch (mType) {
+    case SENSOR_TYPE_ACCELEROMETER:
+        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_AMBIENT_TEMPERATURE:
+        mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_GAME_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GRAVITY:
+        mStringType = SENSOR_STRING_TYPE_GRAVITY;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GYROSCOPE:
+        mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_HEART_RATE: {
+        mStringType = SENSOR_STRING_TYPE_HEART_RATE;
+        mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
+        AppOpsManager appOps;
+        mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        } break;
+    case SENSOR_TYPE_LIGHT:
+        mStringType = SENSOR_STRING_TYPE_LIGHT;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_LINEAR_ACCELERATION:
+        mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_MAGNETIC_FIELD:
+        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_ORIENTATION:
+        mStringType = SENSOR_STRING_TYPE_ORIENTATION;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_PRESSURE:
+        mStringType = SENSOR_STRING_TYPE_PRESSURE;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_PROXIMITY:
+        mStringType = SENSOR_STRING_TYPE_PROXIMITY;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_RELATIVE_HUMIDITY:
+        mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_SIGNIFICANT_MOTION:
+        mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_STEP_COUNTER:
+        mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_STEP_DETECTOR:
+        mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        break;
+    case SENSOR_TYPE_TEMPERATURE:
+        mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_TILT_DETECTOR:
+        mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_WAKE_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_GLANCE_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_PICK_UP_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+        mStringType = SENSOR_STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_WRIST_TILT_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_DYNAMIC_SENSOR_META:
+        mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_POSE_6DOF:
+        mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_STATIONARY_DETECT:
+        mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_MOTION_DETECT:
+        mStringType = SENSOR_STRING_TYPE_MOTION_DETECT;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_HEART_BEAT:
+        mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        break;
+
+    // TODO:  Placeholder for LLOB sensor type
+
+
+    case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    default:
+        // Only pipe the stringType, requiredPermission and flags for custom sensors.
+        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
+            mStringType = hwSensor.stringType;
+        }
+        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
+            mRequiredPermission = hwSensor.requiredPermission;
+            if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
+                AppOpsManager appOps;
+                mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
+            }
+        }
+
+        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags = static_cast<uint32_t>(hwSensor.flags);
+        } else {
+            // This is an OEM defined sensor on an older HAL. Use minDelay to determine the
+            // reporting mode of the sensor.
+            if (mMinDelay > 0) {
+                mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+            } else if (mMinDelay == 0) {
+                mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+            } else if (mMinDelay < 0) {
+                mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+            }
+        }
+        break;
+    }
+
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        // Wake-up flag of HAL 1.3 and above is set here
+        mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP);
+
+        // Log error if the reporting mode is not as expected, but respect HAL setting.
+        int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
+        int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
+        if (actualReportingMode != expectedReportingMode) {
+            ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
+                   "actual=%d expected=%d",
+                   mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
+        }
+    }
+
+    // Feature flags
+    // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK);
+    }
+    // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        // only on continuous sensors direct report mode is defined
+        if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) {
+            mFlags |= hwSensor.flags
+                & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL);
+        }
+    }
+    // Set DATA_INJECTION flag here. Defined in HAL 1_4.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
+        mFlags |= (hwSensor.flags & DATA_INJECTION_MASK);
+    }
+
+    if (mRequiredPermission.length() > 0) {
+        // If the sensor is protected by a permission we need to know if it is
+        // a runtime one to determine whether we can use the permission cache.
+        sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+        if (binder != 0) {
+            sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
+            mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
+                    String16(mRequiredPermission));
+        }
+    }
+}
+
+Sensor::~Sensor() {
+}
+
+const String8& Sensor::getName() const {
+    return mName;
+}
+
+const String8& Sensor::getVendor() const {
+    return mVendor;
+}
+
+int32_t Sensor::getHandle() const {
+    return mHandle;
+}
+
+int32_t Sensor::getType() const {
+    return mType;
+}
+
+float Sensor::getMinValue() const {
+    return mMinValue;
+}
+
+float Sensor::getMaxValue() const {
+    return mMaxValue;
+}
+
+float Sensor::getResolution() const {
+    return mResolution;
+}
+
+float Sensor::getPowerUsage() const {
+    return mPower;
+}
+
+int32_t Sensor::getMinDelay() const {
+    return mMinDelay;
+}
+
+nsecs_t Sensor::getMinDelayNs() const {
+    return getMinDelay() * 1000;
+}
+
+int32_t Sensor::getVersion() const {
+    return mVersion;
+}
+
+uint32_t Sensor::getFifoReservedEventCount() const {
+    return mFifoReservedEventCount;
+}
+
+uint32_t Sensor::getFifoMaxEventCount() const {
+    return mFifoMaxEventCount;
+}
+
+const String8& Sensor::getStringType() const {
+    return mStringType;
+}
+
+const String8& Sensor::getRequiredPermission() const {
+    return mRequiredPermission;
+}
+
+bool Sensor::isRequiredPermissionRuntime() const {
+    return mRequiredPermissionRuntime;
+}
+
+int32_t Sensor::getRequiredAppOp() const {
+    return mRequiredAppOp;
+}
+
+int32_t Sensor::getMaxDelay() const {
+    return mMaxDelay;
+}
+
+uint32_t Sensor::getFlags() const {
+    return mFlags;
+}
+
+bool Sensor::isWakeUpSensor() const {
+    return (mFlags & SENSOR_FLAG_WAKE_UP) != 0;
+}
+
+bool Sensor::isDynamicSensor() const {
+    return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
+}
+
+bool Sensor::hasAdditionalInfo() const {
+    return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
+}
+
+int32_t Sensor::getHighestDirectReportRateLevel() const {
+    return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT);
+}
+
+bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const {
+    switch (sharedMemType) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC;
+        default:
+            return false;
+    }
+}
+
+int32_t Sensor::getReportingMode() const {
+    return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
+}
+
+const Sensor::uuid_t& Sensor::getUuid() const {
+    return mUuid;
+}
+
+void Sensor::setId(int32_t id) {
+    mUuid.i64[0] = id;
+    mUuid.i64[1] = 0;
+}
+
+int32_t Sensor::getId() const {
+    return int32_t(mUuid.i64[0]);
+}
+
+size_t Sensor::getFlattenedSize() const {
+    size_t fixedSize =
+            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
+            sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
+            sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
+            sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
+            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
+
+    size_t variableSize =
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
+
+    return fixedSize + variableSize;
+}
+
+status_t Sensor::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    flattenString8(buffer, size, mName);
+    flattenString8(buffer, size, mVendor);
+    FlattenableUtils::write(buffer, size, mVersion);
+    FlattenableUtils::write(buffer, size, mHandle);
+    FlattenableUtils::write(buffer, size, mType);
+    FlattenableUtils::write(buffer, size, mMinValue);
+    FlattenableUtils::write(buffer, size, mMaxValue);
+    FlattenableUtils::write(buffer, size, mResolution);
+    FlattenableUtils::write(buffer, size, mPower);
+    FlattenableUtils::write(buffer, size, mMinDelay);
+    FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
+    FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
+    flattenString8(buffer, size, mStringType);
+    flattenString8(buffer, size, mRequiredPermission);
+    FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime);
+    FlattenableUtils::write(buffer, size, mRequiredAppOp);
+    FlattenableUtils::write(buffer, size, mMaxDelay);
+    FlattenableUtils::write(buffer, size, mFlags);
+    if (mUuid.i64[1] != 0) {
+        // We should never hit this case with our current API, but we
+        // could via a careless API change.  If that happens,
+        // this code will keep us from leaking our UUID (while probably
+        // breaking dynamic sensors).  See b/29547335.
+        ALOGW("Sensor with UUID being flattened; sending 0.  Expect "
+              "bad dynamic sensor behavior");
+        uuid_t tmpUuid;  // default constructor makes this 0.
+        FlattenableUtils::write(buffer, size, tmpUuid);
+    } else {
+        FlattenableUtils::write(buffer, size, mUuid);
+    }
+    return NO_ERROR;
+}
+
+status_t Sensor::unflatten(void const* buffer, size_t size) {
+    if (!unflattenString8(buffer, size, mName)) {
+        return NO_MEMORY;
+    }
+    if (!unflattenString8(buffer, size, mVendor)) {
+        return NO_MEMORY;
+    }
+
+    size_t fixedSize1 =
+            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) +
+            sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) +
+            sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount);
+    if (size < fixedSize1) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mVersion);
+    FlattenableUtils::read(buffer, size, mHandle);
+    FlattenableUtils::read(buffer, size, mType);
+    FlattenableUtils::read(buffer, size, mMinValue);
+    FlattenableUtils::read(buffer, size, mMaxValue);
+    FlattenableUtils::read(buffer, size, mResolution);
+    FlattenableUtils::read(buffer, size, mPower);
+    FlattenableUtils::read(buffer, size, mMinDelay);
+    FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
+    FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
+
+    if (!unflattenString8(buffer, size, mStringType)) {
+        return NO_MEMORY;
+    }
+    if (!unflattenString8(buffer, size, mRequiredPermission)) {
+        return NO_MEMORY;
+    }
+
+    size_t fixedSize2 =
+            sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
+            sizeof(mFlags) + sizeof(mUuid);
+    if (size < fixedSize2) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime);
+    FlattenableUtils::read(buffer, size, mRequiredAppOp);
+    FlattenableUtils::read(buffer, size, mMaxDelay);
+    FlattenableUtils::read(buffer, size, mFlags);
+    FlattenableUtils::read(buffer, size, mUuid);
+    return NO_ERROR;
+}
+
+void Sensor::flattenString8(void*& buffer, size_t& size,
+        const String8& string8) {
+    uint32_t len = static_cast<uint32_t>(string8.length());
+    FlattenableUtils::write(buffer, size, len);
+    memcpy(static_cast<char*>(buffer), string8.string(), len);
+    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+}
+
+bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
+    uint32_t len;
+    if (size < sizeof(len)) {
+        return false;
+    }
+    FlattenableUtils::read(buffer, size, len);
+    if (size < len) {
+        return false;
+    }
+    outputString8.setTo(static_cast<char const*>(buffer), len);
+    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
new file mode 100644
index 0000000..6f68fb5
--- /dev/null
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensors"
+
+#include <sensor/SensorEventQueue.h>
+
+#include <algorithm>
+#include <sys/socket.h>
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorEventConnection.h>
+
+#include <android/sensor.h>
+
+using std::min;
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
+    : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
+      mNumAcksToSend(0) {
+    mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
+}
+
+SensorEventQueue::~SensorEventQueue() {
+    delete [] mRecBuffer;
+}
+
+void SensorEventQueue::onFirstRef()
+{
+    mSensorChannel = mSensorEventConnection->getSensorChannel();
+}
+
+int SensorEventQueue::getFd() const
+{
+    return mSensorChannel->getFd();
+}
+
+
+ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
+        ASensorEvent const* events, size_t numEvents) {
+    return BitTube::sendObjects(tube, events, numEvents);
+}
+
+ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
+    if (mAvailable == 0) {
+        ssize_t err = BitTube::recvObjects(mSensorChannel,
+                mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
+        if (err < 0) {
+            return err;
+        }
+        mAvailable = static_cast<size_t>(err);
+        mConsumed = 0;
+    }
+    size_t count = min(numEvents, mAvailable);
+    memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
+    mAvailable -= count;
+    mConsumed += count;
+    return static_cast<ssize_t>(count);
+}
+
+sp<Looper> SensorEventQueue::getLooper() const
+{
+    Mutex::Autolock _l(mLock);
+    if (mLooper == 0) {
+        mLooper = new Looper(true);
+        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
+    }
+    return mLooper;
+}
+
+status_t SensorEventQueue::waitForEvent() const
+{
+    const int fd = getFd();
+    sp<Looper> looper(getLooper());
+
+    int events;
+    int32_t result;
+    do {
+        result = looper->pollOnce(-1, NULL, &events, NULL);
+        if (result == ALOOPER_POLL_ERROR) {
+            ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
+            result = -EPIPE; // unknown error, so we make up one
+            break;
+        }
+        if (events & ALOOPER_EVENT_HANGUP) {
+            // the other-side has died
+            ALOGE("SensorEventQueue::waitForEvent error HANGUP");
+            result = -EPIPE; // unknown error, so we make up one
+            break;
+        }
+    } while (result != fd);
+
+    return  (result == fd) ? status_t(NO_ERROR) : result;
+}
+
+status_t SensorEventQueue::wake() const
+{
+    sp<Looper> looper(getLooper());
+    looper->wake();
+    return NO_ERROR;
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
+    return enableSensor(sensor, SENSOR_DELAY_NORMAL);
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const {
+    return mSensorEventConnection->enableDisable(sensor->getHandle(), true,
+                                                 us2ns(samplingPeriodUs), 0, 0);
+}
+
+status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
+    return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0);
+}
+
+status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
+                                        int64_t maxBatchReportLatencyUs, int reservedFlags) const {
+    return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
+                                                 us2ns(maxBatchReportLatencyUs), reservedFlags);
+}
+
+status_t SensorEventQueue::flush() const {
+    return mSensorEventConnection->flush();
+}
+
+status_t SensorEventQueue::disableSensor(int32_t handle) const {
+    return mSensorEventConnection->enableDisable(handle, false, 0, 0, false);
+}
+
+status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
+    return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
+}
+
+status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
+    do {
+        // Blocking call.
+        ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
+        if (size >= 0) {
+            return NO_ERROR;
+        } else if (size < 0 && errno == EAGAIN) {
+            // If send is returning a "Try again" error, sleep for 100ms and try again. In all
+            // other cases log a failure and exit.
+            usleep(100000);
+        } else {
+            ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size);
+            return INVALID_OPERATION;
+        }
+    } while (true);
+}
+
+void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {
+            ++mNumAcksToSend;
+        }
+    }
+    // Send mNumAcksToSend to acknowledge for the wake up sensor events received.
+    if (mNumAcksToSend > 0) {
+        ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend),
+                MSG_DONTWAIT | MSG_NOSIGNAL);
+        if (size < 0) {
+            ALOGE("sendAck failure %zd %d", size, mNumAcksToSend);
+        } else {
+            mNumAcksToSend = 0;
+        }
+    }
+    return;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
new file mode 100644
index 0000000..3fbc5eb
--- /dev/null
+++ b/libs/sensor/SensorManager.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensors"
+
+#include <sensor/SensorManager.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+Mutex SensorManager::sLock;
+std::map<String16, SensorManager*> SensorManager::sPackageInstances;
+
+SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
+    waitForSensorService(nullptr);
+
+    Mutex::Autolock _l(sLock);
+    SensorManager* sensorManager;
+    auto iterator = sPackageInstances.find(packageName);
+
+    if (iterator != sPackageInstances.end()) {
+        sensorManager = iterator->second;
+    } else {
+        String16 opPackageName = packageName;
+
+        // It is possible that the calling code has no access to the package name.
+        // In this case we will get the packages for the calling UID and pick the
+        // first one for attributing the app op. This will work correctly for
+        // runtime permissions as for legacy apps we will toggle the app op for
+        // all packages in the UID. The caveat is that the operation may be attributed
+        // to the wrong package and stats based on app ops may be slightly off.
+        if (opPackageName.size() <= 0) {
+            sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+            if (binder != 0) {
+                const uid_t uid = IPCThreadState::self()->getCallingUid();
+                Vector<String16> packages;
+                interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
+                if (!packages.isEmpty()) {
+                    opPackageName = packages[0];
+                } else {
+                    ALOGE("No packages for calling UID");
+                }
+            } else {
+                ALOGE("Cannot get permission service");
+            }
+        }
+
+        sensorManager = new SensorManager(opPackageName);
+
+        // If we had no package name, we looked it up from the UID and the sensor
+        // manager instance we created should also be mapped to the empty package
+        // name, to avoid looking up the packages for a UID and get the same result.
+        if (packageName.size() <= 0) {
+            sPackageInstances.insert(std::make_pair(String16(), sensorManager));
+        }
+
+        // Stash the per package sensor manager.
+        sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
+    }
+
+    return *sensorManager;
+}
+
+SensorManager::SensorManager(const String16& opPackageName)
+    : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+    // okay we're not locked here, but it's not needed during construction
+    assertStateLocked();
+}
+
+SensorManager::~SensorManager() {
+    free(mSensorList);
+}
+
+status_t SensorManager::waitForSensorService(sp<ISensorServer> *server) {
+    // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
+    sp<ISensorServer> s;
+    const String16 name("sensorservice");
+    for (int i = 0; i < 60; i++) {
+        status_t err = getService(name, &s);
+        switch (err) {
+            case NAME_NOT_FOUND:
+                sleep(1);
+                continue;
+            case NO_ERROR:
+                if (server != nullptr) {
+                    *server = s;
+                }
+                return NO_ERROR;
+            default:
+                return err;
+        }
+    }
+    return TIMED_OUT;
+}
+
+void SensorManager::sensorManagerDied() {
+    Mutex::Autolock _l(mLock);
+    mSensorServer.clear();
+    free(mSensorList);
+    mSensorList = NULL;
+    mSensors.clear();
+}
+
+status_t SensorManager::assertStateLocked() {
+    bool initSensorManager = false;
+    if (mSensorServer == NULL) {
+        initSensorManager = true;
+    } else {
+        // Ping binder to check if sensorservice is alive.
+        status_t err = IInterface::asBinder(mSensorServer)->pingBinder();
+        if (err != NO_ERROR) {
+            initSensorManager = true;
+        }
+    }
+    if (initSensorManager) {
+        waitForSensorService(&mSensorServer);
+        LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL");
+
+        class DeathObserver : public IBinder::DeathRecipient {
+            SensorManager& mSensorManager;
+            virtual void binderDied(const wp<IBinder>& who) {
+                ALOGW("sensorservice died [%p]", who.unsafe_get());
+                mSensorManager.sensorManagerDied();
+            }
+        public:
+            explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
+        };
+
+        mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
+        IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
+
+        mSensors = mSensorServer->getSensorList(mOpPackageName);
+        size_t count = mSensors.size();
+        mSensorList =
+                static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
+        LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
+
+        for (size_t i=0 ; i<count ; i++) {
+            mSensorList[i] = mSensors.array() + i;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+ssize_t SensorManager::getSensorList(Sensor const* const** list) {
+    Mutex::Autolock _l(mLock);
+    status_t err = assertStateLocked();
+    if (err < 0) {
+        return static_cast<ssize_t>(err);
+    }
+    *list = mSensorList;
+    return static_cast<ssize_t>(mSensors.size());
+}
+
+ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
+    Mutex::Autolock _l(mLock);
+    status_t err = assertStateLocked();
+    if (err < 0) {
+        return static_cast<ssize_t>(err);
+    }
+
+    dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName);
+    size_t count = dynamicSensors.size();
+
+    return static_cast<ssize_t>(count);
+}
+
+Sensor const* SensorManager::getDefaultSensor(int type)
+{
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        bool wakeUpSensor = false;
+        // For the following sensor types, return a wake-up sensor. These types are by default
+        // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
+        // a non_wake-up version.
+        if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+            type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
+            type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
+            type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
+            type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
+            wakeUpSensor = true;
+        }
+        // For now we just return the first sensor of that type we find.
+        // in the future it will make sense to let the SensorService make
+        // that decision.
+        for (size_t i=0 ; i<mSensors.size() ; i++) {
+            if (mSensorList[i]->getType() == type &&
+                mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
+                return mSensorList[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
+    sp<SensorEventQueue> queue;
+
+    Mutex::Autolock _l(mLock);
+    while (assertStateLocked() == NO_ERROR) {
+        sp<ISensorEventConnection> connection =
+                mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
+        if (connection == NULL) {
+            // SensorService just died or the app doesn't have required permissions.
+            ALOGE("createEventQueue: connection is NULL.");
+            return NULL;
+        }
+        queue = new SensorEventQueue(connection);
+        break;
+    }
+    return queue;
+}
+
+bool SensorManager::isDataInjectionEnabled() {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        return mSensorServer->isDataInjectionEnabled();
+    }
+    return false;
+}
+
+int SensorManager::createDirectChannel(
+        size_t size, int channelType, const native_handle_t *resourceHandle) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() != NO_ERROR) {
+        return NO_INIT;
+    }
+
+    if (channelType != SENSOR_DIRECT_MEM_TYPE_ASHMEM
+            && channelType != SENSOR_DIRECT_MEM_TYPE_GRALLOC) {
+        ALOGE("Bad channel shared memory type %d", channelType);
+        return BAD_VALUE;
+    }
+
+    sp<ISensorEventConnection> conn =
+              mSensorServer->createSensorDirectConnection(mOpPackageName,
+                  static_cast<uint32_t>(size),
+                  static_cast<int32_t>(channelType),
+                  SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
+    if (conn == nullptr) {
+        return NO_MEMORY;
+    }
+
+    int nativeHandle = mDirectConnectionHandle++;
+    mDirectConnection.emplace(nativeHandle, conn);
+    return nativeHandle;
+}
+
+void SensorManager::destroyDirectChannel(int channelNativeHandle) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        mDirectConnection.erase(channelNativeHandle);
+    }
+}
+
+int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() != NO_ERROR) {
+        return NO_INIT;
+    }
+
+    auto i = mDirectConnection.find(channelNativeHandle);
+    if (i == mDirectConnection.end()) {
+        ALOGE("Cannot find the handle in client direct connection table");
+        return BAD_VALUE;
+    }
+
+    int ret;
+    ret = i->second->configureChannel(sensorHandle, rateLevel);
+    ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d",
+            static_cast<int>(sensorHandle), static_cast<int>(rateLevel),
+            static_cast<int>(ret));
+    return ret;
+}
+
+int SensorManager::setOperationParameter(
+        int type, const Vector<float> &floats, const Vector<int32_t> &ints) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() != NO_ERROR) {
+        return NO_INIT;
+    }
+    return mSensorServer->setOperationParameter(type, floats, ints);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/BitTube.h b/libs/sensor/include/sensor/BitTube.h
new file mode 100644
index 0000000..c1fabe8
--- /dev/null
+++ b/libs/sensor/include/sensor/BitTube.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+class Parcel;
+
+class BitTube : public RefBase
+{
+public:
+
+    // creates a BitTube with a default (4KB) send buffer
+    BitTube();
+
+    // creates a BitTube with a a specified send and receive buffer size
+    explicit BitTube(size_t bufsize);
+
+    explicit BitTube(const Parcel& data);
+    virtual ~BitTube();
+
+    // check state after construction
+    status_t initCheck() const;
+
+    // get receive file-descriptor
+    int getFd() const;
+
+    // get the send file-descriptor.
+    int getSendFd() const;
+
+    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+    template <typename T>
+    static ssize_t sendObjects(const sp<BitTube>& tube,
+            T const* events, size_t count) {
+        return sendObjects(tube, events, count, sizeof(T));
+    }
+
+    // receive objects (sized blobs). If the receiving buffer isn't large enough,
+    // excess messages are silently discarded.
+    template <typename T>
+    static ssize_t recvObjects(const sp<BitTube>& tube,
+            T* events, size_t count) {
+        return recvObjects(tube, events, count, sizeof(T));
+    }
+
+    // parcels this BitTube
+    status_t writeToParcel(Parcel* reply) const;
+
+private:
+    void init(size_t rcvbuf, size_t sndbuf);
+
+    // send a message. The write is guaranteed to send the whole message or fail.
+    ssize_t write(void const* vaddr, size_t size);
+
+    // receive a message. the passed buffer must be at least as large as the
+    // write call used to send the message, excess data is silently discarded.
+    ssize_t read(void* vaddr, size_t size);
+
+    int mSendFd;
+    mutable int mReceiveFd;
+
+    static ssize_t sendObjects(const sp<BitTube>& tube,
+            void const* events, size_t count, size_t objSize);
+
+    static ssize_t recvObjects(const sp<BitTube>& tube,
+            void* events, size_t count, size_t objSize);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorEventConnection.h b/libs/sensor/include/sensor/ISensorEventConnection.h
new file mode 100644
index 0000000..07cc7e8
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorEventConnection.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BitTube;
+class Parcel;
+
+class ISensorEventConnection : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(SensorEventConnection)
+
+    virtual sp<BitTube> getSensorChannel() const = 0;
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
+    virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
+    virtual status_t flush() = 0;
+    virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
new file mode 100644
index 0000000..8d50062
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include <binder/IInterface.h>
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Parcel;
+class Sensor;
+class String8;
+class String16;
+
+class ISensorServer : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(SensorServer)
+
+    virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
+    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+
+    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
+             int mode, const String16& opPackageName) = 0;
+    virtual int32_t isDataInjectionEnabled() = 0;
+
+    virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+
+    virtual int setOperationParameter(
+            int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorServer : public BnInterface<ISensorServer>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
new file mode 100644
index 0000000..043e635
--- /dev/null
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Flattenable.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+// FIXME: including from android/ breaks layering, as libandroid ultimately depends on libsensors
+#include <android/sensor.h>
+
+#include <hardware/sensors.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensor { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Parcel;
+
+// ----------------------------------------------------------------------------
+
+class Sensor : public ASensor, public LightFlattenable<Sensor>
+{
+public:
+    enum {
+        TYPE_ACCELEROMETER  = ASENSOR_TYPE_ACCELEROMETER,
+        TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+        TYPE_GYROSCOPE      = ASENSOR_TYPE_GYROSCOPE,
+        TYPE_LIGHT          = ASENSOR_TYPE_LIGHT,
+        TYPE_PROXIMITY      = ASENSOR_TYPE_PROXIMITY
+    };
+
+    struct uuid_t{
+        union {
+            uint8_t b[16];
+            int64_t i64[2];
+        };
+        uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
+        uuid_t() : b{0} {}
+    };
+
+    Sensor(const Sensor&) = default;
+    Sensor& operator=(const Sensor&) = default;
+
+    Sensor(const char * name = "");
+    Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
+    Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
+    ~Sensor();
+
+    const String8& getName() const;
+    const String8& getVendor() const;
+    int32_t getHandle() const;
+    int32_t getType() const;
+    float getMinValue() const;
+    float getMaxValue() const;
+    float getResolution() const;
+    float getPowerUsage() const;
+    int32_t getMinDelay() const;
+    nsecs_t getMinDelayNs() const;
+    int32_t getVersion() const;
+    uint32_t getFifoReservedEventCount() const;
+    uint32_t getFifoMaxEventCount() const;
+    const String8& getStringType() const;
+    const String8& getRequiredPermission() const;
+    bool isRequiredPermissionRuntime() const;
+    int32_t getRequiredAppOp() const;
+    int32_t getMaxDelay() const;
+    uint32_t getFlags() const;
+    bool isWakeUpSensor() const;
+    bool isDynamicSensor() const;
+    bool hasAdditionalInfo() const;
+    int32_t getHighestDirectReportRateLevel() const;
+    bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
+    int32_t getReportingMode() const;
+
+    // Note that after setId() has been called, getUuid() no longer
+    // returns the UUID.
+    // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
+    //     make sure setId() doesn't change the UuidIndex.
+    const uuid_t& getUuid() const;
+    int32_t getId() const;
+    void setId(int32_t id);
+
+    // LightFlattenable protocol
+    inline bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
+
+private:
+    String8 mName;
+    String8 mVendor;
+    int32_t mHandle;
+    int32_t mType;
+    float   mMinValue;
+    float   mMaxValue;
+    float   mResolution;
+    float   mPower;
+    int32_t mMinDelay;
+    int32_t mVersion;
+    uint32_t mFifoReservedEventCount;
+    uint32_t mFifoMaxEventCount;
+    String8 mStringType;
+    String8 mRequiredPermission;
+    bool mRequiredPermissionRuntime = false;
+    int32_t mRequiredAppOp;
+    int32_t mMaxDelay;
+    uint32_t mFlags;
+    // TODO(b/29547335): Get rid of this field and replace with an index.
+    //     The index will be into a separate global vector of UUIDs.
+    //     Also add an mId field (and change flatten/unflatten appropriately).
+    uuid_t  mUuid;
+    static void flattenString8(void*& buffer, size_t& size, const String8& string8);
+    static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
new file mode 100644
index 0000000..baed2ee
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Mutex.h>
+
+#include <sensor/BitTube.h>
+
+// ----------------------------------------------------------------------------
+#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
+struct ALooper;
+struct ASensorEvent;
+
+// Concrete types for the NDK
+struct ASensorEventQueue {
+    ALooper* looper;
+};
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Sensor;
+class Looper;
+
+// ----------------------------------------------------------------------------
+
+class SensorEventQueue : public ASensorEventQueue, public RefBase
+{
+public:
+
+    enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
+
+    /**
+     * Typical sensor delay (sample period) in microseconds.
+     */
+    // Fastest sampling, system will bound it to minDelay
+    static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
+    // Typical sample period for game, 50Hz;
+    static constexpr int32_t SENSOR_DELAY_GAME = 20000;
+    // Typical sample period for UI, 15Hz
+    static constexpr int32_t SENSOR_DELAY_UI = 66667;
+    // Default sensor sample period
+    static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
+
+    SensorEventQueue(const sp<ISensorEventConnection>& connection);
+    virtual ~SensorEventQueue();
+    virtual void onFirstRef();
+
+    int getFd() const;
+
+    static ssize_t write(const sp<BitTube>& tube,
+            ASensorEvent const* events, size_t numEvents);
+
+    ssize_t read(ASensorEvent* events, size_t numEvents);
+
+    status_t waitForEvent() const;
+    status_t wake() const;
+
+    status_t enableSensor(Sensor const* sensor) const;
+    status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
+    status_t disableSensor(Sensor const* sensor) const;
+    status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+
+    // these are here only to support SensorManager.java and HIDL Frameworks SensorManager.
+    status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs,
+                          int reservedFlags) const;
+    status_t disableSensor(int32_t handle) const;
+    status_t flush() const;
+    // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
+    void sendAck(const ASensorEvent* events, int count);
+
+    status_t injectSensorEvent(const ASensorEvent& event);
+private:
+    sp<Looper> getLooper() const;
+    sp<ISensorEventConnection> mSensorEventConnection;
+    sp<BitTube> mSensorChannel;
+    mutable Mutex mLock;
+    mutable sp<Looper> mLooper;
+    ASensorEvent* mRecBuffer;
+    size_t mAvailable;
+    size_t mConsumed;
+    uint32_t mNumAcksToSend;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
new file mode 100644
index 0000000..5fc85d3
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_SENSOR_MANAGER_H
+#define ANDROID_GUI_SENSOR_MANAGER_H
+
+#include <map>
+#include <unordered_map>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+#include <sensor/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensorManager { };
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorServer;
+class Sensor;
+class SensorEventQueue;
+// ----------------------------------------------------------------------------
+
+class SensorManager : public ASensorManager
+{
+public:
+    static SensorManager& getInstanceForPackage(const String16& packageName);
+    ~SensorManager();
+
+    ssize_t getSensorList(Sensor const* const** list);
+    ssize_t getDynamicSensorList(Vector<Sensor>& list);
+    Sensor const* getDefaultSensor(int type);
+    sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
+    bool isDataInjectionEnabled();
+    int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+    void destroyDirectChannel(int channelNativeHandle);
+    int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
+    int setOperationParameter(int type, const Vector<float> &floats, const Vector<int32_t> &ints);
+
+private:
+    // DeathRecipient interface
+    void sensorManagerDied();
+    static status_t waitForSensorService(sp<ISensorServer> *server);
+
+    SensorManager(const String16& opPackageName);
+    status_t assertStateLocked();
+
+private:
+    static Mutex sLock;
+    static std::map<String16, SensorManager*> sPackageInstances;
+
+    Mutex mLock;
+    sp<ISensorServer> mSensorServer;
+    Sensor const** mSensorList;
+    Vector<Sensor> mSensors;
+    sp<IBinder::DeathRecipient> mDeathObserver;
+    const String16 mOpPackageName;
+    std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+    int32_t mDirectConnectionHandle;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
new file mode 100644
index 0000000..9d530fc
--- /dev/null
+++ b/libs/sensor/tests/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+cc_test {
+    name: "libsensor_test",
+
+    clang: true,
+
+    srcs: [
+        "Sensor_test.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libsensor",
+        "libutils",
+    ],
+}
diff --git a/libs/sensor/tests/Sensor_test.cpp b/libs/sensor/tests/Sensor_test.cpp
new file mode 100644
index 0000000..ede20c9
--- /dev/null
+++ b/libs/sensor/tests/Sensor_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensor_test"
+
+#include <sensor/Sensor.h>
+#include <hardware/sensors.h>
+#include <utils/Errors.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// Returns true if the two sensors have the same attributes. Does not compare
+// UUID since that should not be transmitted via flatten/unflatten.
+static bool sensorsMatch(const Sensor& a, const Sensor& b) {
+    return a.getName() == b.getName () &&
+        a.getVendor() == b.getVendor () &&
+        a.getHandle() == b.getHandle () &&
+        a.getType() == b.getType () &&
+        a.getMinValue() == b.getMinValue () &&
+        a.getMaxValue() == b.getMaxValue () &&
+        a.getResolution() == b.getResolution () &&
+        a.getPowerUsage() == b.getPowerUsage () &&
+        a.getMinDelay() == b.getMinDelay () &&
+        a.getMinDelayNs() == b.getMinDelayNs () &&
+        a.getVersion() == b.getVersion () &&
+        a.getFifoReservedEventCount() == b.getFifoReservedEventCount () &&
+        a.getFifoMaxEventCount() == b.getFifoMaxEventCount () &&
+        a.getStringType() == b.getStringType () &&
+        a.getRequiredPermission() == b.getRequiredPermission () &&
+        a.isRequiredPermissionRuntime() == b.isRequiredPermissionRuntime () &&
+        a.getRequiredAppOp() == b.getRequiredAppOp () &&
+        a.getMaxDelay() == b.getMaxDelay () &&
+        a.getFlags() == b.getFlags () &&
+        a.isWakeUpSensor() == b.isWakeUpSensor () &&
+        a.isDynamicSensor() == b.isDynamicSensor () &&
+        a.hasAdditionalInfo() == b.hasAdditionalInfo () &&
+        a.getReportingMode() == b.getReportingMode();
+}
+
+// Creates and returns a sensor_t struct with some default values filled in.
+static sensor_t getTestSensorT() {
+    sensor_t hwSensor = {};
+    hwSensor.name = "Test Sensor";
+    hwSensor.vendor = "Test Vendor";
+    hwSensor.version = 1;
+    hwSensor.handle = 2;
+    hwSensor.type = SENSOR_TYPE_ACCELEROMETER;
+    hwSensor.maxRange = 10.f;
+    hwSensor.resolution = 1.f;
+    hwSensor.power = 5.f;
+    hwSensor.minDelay = 1000;
+    hwSensor.fifoReservedEventCount = 50;
+    hwSensor.fifoMaxEventCount = 100;
+    hwSensor.stringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+    hwSensor.requiredPermission = "";
+    hwSensor.maxDelay = 5000;
+    hwSensor.flags = SENSOR_FLAG_CONTINUOUS_MODE;
+    return hwSensor;
+}
+
+TEST(SensorTest, FlattenAndUnflatten) {
+    sensor_t hwSensor = getTestSensorT();
+    Sensor sensor1(&hwSensor, SENSORS_DEVICE_API_VERSION_1_4);
+    Sensor sensor2;
+
+    std::vector<uint8_t> buffer(sensor1.getFlattenedSize());
+    ASSERT_EQ(OK, sensor1.flatten(buffer.data(), buffer.size()));
+    ASSERT_EQ(OK, sensor2.unflatten(buffer.data(), buffer.size()));
+
+    EXPECT_TRUE(sensorsMatch(sensor1, sensor2));
+}
+
+} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
new file mode 100644
index 0000000..5edd664
--- /dev/null
+++ b/libs/ui/Android.bp
@@ -0,0 +1,98 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libui",
+    vendor_available: true,
+
+    clang: true,
+    cppflags: [
+        "-Weverything",
+        "-Werror",
+
+        // The static constructors and destructors in this library have not been noted to
+        // introduce significant overheads
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+
+        // We only care about compiling as C++14
+        "-Wno-c++98-compat-pedantic",
+
+        // We are aware of the risks inherent in comparing floats for equality
+        "-Wno-float-equal",
+
+        // We use four-character constants for the GraphicBuffer header, and don't care
+        // that they're non-portable as long as they're consistent within one execution
+        "-Wno-four-char-constants",
+
+        // Don't warn about struct padding
+        "-Wno-padded",
+    ],
+
+    sanitize: {
+        //misc_undefined: ["integer"],
+    },
+
+    srcs: [
+        "ColorSpace.cpp",
+        "DebugUtils.cpp",
+        "Fence.cpp",
+        "FenceTime.cpp",
+        "FrameStats.cpp",
+        "Gralloc2.cpp",
+        "GraphicBuffer.cpp",
+        "GraphicBufferAllocator.cpp",
+        "GraphicBufferMapper.cpp",
+        "GraphicsEnv.cpp",
+        "HdrCapabilities.cpp",
+        "PixelFormat.cpp",
+        "Rect.cpp",
+        "Region.cpp",
+        "UiConfig.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/native/include",
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.configstore@1.0",
+	"android.hardware.configstore-utils",
+        "libbase",
+        "libnativeloader",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libsync",
+        "libutils",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libarect",
+        "libgrallocusage",
+        "libmath",
+    ],
+
+    export_static_lib_headers: [
+        "libarect",
+        "libmath",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
deleted file mode 100644
index e690ede..0000000
--- a/libs/ui/Android.mk
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -std=c++1y -Weverything -Werror
-# LOCAL_SANITIZE := integer
-
-# The static constructors and destructors in this library have not been noted to
-# introduce significant overheads
-LOCAL_CPPFLAGS += -Wno-exit-time-destructors
-LOCAL_CPPFLAGS += -Wno-global-constructors
-
-# We only care about compiling as C++14
-LOCAL_CPPFLAGS += -Wno-c++98-compat-pedantic
-
-# We use four-character constants for the GraphicBuffer header, and don't care
-# that they're non-portable as long as they're consistent within one execution
-LOCAL_CPPFLAGS += -Wno-four-char-constants
-
-# Don't warn about struct padding
-LOCAL_CPPFLAGS += -Wno-padded
-
-LOCAL_SRC_FILES := \
-	Fence.cpp \
-	FrameStats.cpp \
-	Gralloc1.cpp \
-	Gralloc1On0Adapter.cpp \
-	GraphicBuffer.cpp \
-	GraphicBufferAllocator.cpp \
-	GraphicBufferMapper.cpp \
-	HdrCapabilities.cpp \
-	PixelFormat.cpp \
-	Rect.cpp \
-	Region.cpp \
-	UiConfig.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libbinder \
-	libcutils \
-	libhardware \
-	libsync \
-	libutils \
-	liblog
-
-ifneq ($(BOARD_FRAMEBUFFER_FORCE_FORMAT),)
-LOCAL_CFLAGS += -DFRAMEBUFFER_FORCE_FORMAT=$(BOARD_FRAMEBUFFER_FORCE_FORMAT)
-endif
-
-LOCAL_MODULE := libui
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
new file mode 100644
index 0000000..5b4bf23
--- /dev/null
+++ b/libs/ui/ColorSpace.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/ColorSpace.h>
+
+using namespace std::placeholders;
+
+namespace android {
+
+static constexpr float linearResponse(float v) {
+    return v;
+}
+
+static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
+}
+
+static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
+}
+
+static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
+}
+
+static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+    float xx = std::abs(x);
+    return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+   float xx = std::abs(x);
+   return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
+}
+
+static float safePow(float x, float e) {
+    return powf(x < 0.0f ? 0.0f : x, e);
+}
+
+static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
+    if (parameters.e == 0.0f && parameters.f == 0.0f) {
+        return std::bind(rcpResponse, _1, parameters);
+    }
+    return std::bind(rcpFullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
+    if (parameters.e == 0.0f && parameters.f == 0.0f) {
+        return std::bind(response, _1, parameters);
+    }
+    return std::bind(fullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toOETF(float gamma) {
+    if (gamma == 1.0f) {
+        return linearResponse;
+    }
+    return std::bind(safePow, _1, 1.0f / gamma);
+}
+
+static ColorSpace::transfer_function toEOTF(float gamma) {
+    if (gamma == 1.0f) {
+        return linearResponse;
+    }
+    return std::bind(safePow, _1, gamma);
+}
+
+static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
+    float3 r(rgbToXYZ * float3{1, 0, 0});
+    float3 g(rgbToXYZ * float3{0, 1, 0});
+    float3 b(rgbToXYZ * float3{0, 0, 1});
+
+    return {{r.xy / dot(r, float3{1}),
+             g.xy / dot(g, float3{1}),
+             b.xy / dot(b, float3{1})}};
+}
+
+static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
+    float3 w(rgbToXYZ * float3{1});
+    return w.xy / dot(w, float3{1});
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const mat3& rgbToXYZ,
+        transfer_function OETF,
+        transfer_function EOTF,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(rgbToXYZ)
+        , mXYZtoRGB(inverse(rgbToXYZ))
+        , mOETF(std::move(OETF))
+        , mEOTF(std::move(EOTF))
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const mat3& rgbToXYZ,
+        const TransferParameters parameters,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(rgbToXYZ)
+        , mXYZtoRGB(inverse(rgbToXYZ))
+        , mParameters(parameters)
+        , mOETF(toOETF(mParameters))
+        , mEOTF(toEOTF(mParameters))
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const mat3& rgbToXYZ,
+        float gamma,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(rgbToXYZ)
+        , mXYZtoRGB(inverse(rgbToXYZ))
+        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+        , mOETF(toOETF(gamma))
+        , mEOTF(toEOTF(gamma))
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const std::array<float2, 3>& primaries,
+        const float2& whitePoint,
+        transfer_function OETF,
+        transfer_function EOTF,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+        , mXYZtoRGB(inverse(mRGBtoXYZ))
+        , mOETF(std::move(OETF))
+        , mEOTF(std::move(EOTF))
+        , mClamper(std::move(clamper))
+        , mPrimaries(primaries)
+        , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const std::array<float2, 3>& primaries,
+        const float2& whitePoint,
+        const TransferParameters parameters,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+        , mXYZtoRGB(inverse(mRGBtoXYZ))
+        , mParameters(parameters)
+        , mOETF(toOETF(mParameters))
+        , mEOTF(toEOTF(mParameters))
+        , mClamper(std::move(clamper))
+        , mPrimaries(primaries)
+        , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const std::array<float2, 3>& primaries,
+        const float2& whitePoint,
+        float gamma,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+        , mXYZtoRGB(inverse(mRGBtoXYZ))
+        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+        , mOETF(toOETF(gamma))
+        , mEOTF(toEOTF(gamma))
+        , mClamper(std::move(clamper))
+        , mPrimaries(primaries)
+        , mWhitePoint(whitePoint) {
+}
+
+constexpr mat3 ColorSpace::computeXYZMatrix(
+        const std::array<float2, 3>& primaries, const float2& whitePoint) {
+    const float2& R = primaries[0];
+    const float2& G = primaries[1];
+    const float2& B = primaries[2];
+    const float2& W = whitePoint;        
+
+    float oneRxRy = (1 - R.x) / R.y;
+    float oneGxGy = (1 - G.x) / G.y;
+    float oneBxBy = (1 - B.x) / B.y;
+    float oneWxWy = (1 - W.x) / W.y;
+
+    float RxRy = R.x / R.y;
+    float GxGy = G.x / G.y;
+    float BxBy = B.x / B.y;
+    float WxWy = W.x / W.y;
+
+    float BY =
+            ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
+            ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
+    float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
+    float RY = 1 - GY - BY;
+
+    float RYRy = RY / R.y;
+    float GYGy = GY / G.y;
+    float BYBy = BY / B.y;
+
+    return {
+        float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
+        float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
+        float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
+    };
+}
+
+const ColorSpace ColorSpace::sRGB() {
+    return {
+        "sRGB IEC61966-2.1",
+        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::linearSRGB() {
+    return {
+        "sRGB IEC61966-2.1 (Linear)",
+        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f}
+    };
+}
+
+const ColorSpace ColorSpace::extendedSRGB() {
+    return {
+        "scRGB-nl IEC 61966-2-2:2003",
+        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+        std::bind(absResponse,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+        std::bind(clamp<float>, _1, -0.799f, 2.399f)
+    };
+}
+
+const ColorSpace ColorSpace::linearExtendedSRGB() {
+    return {
+        "scRGB IEC 61966-2-2:2003",
+        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        1.0f,
+        std::bind(clamp<float>, _1, -0.5f, 7.499f)
+    };
+}
+
+const ColorSpace ColorSpace::NTSC() {
+    return {
+        "NTSC (1953)",
+        {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
+        {0.310f, 0.316f},
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::BT709() {
+    return {
+        "Rec. ITU-R BT.709-5",
+        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::BT2020() {
+    return {
+        "Rec. ITU-R BT.2020-1",
+        {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
+        {0.3127f, 0.3290f},
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::AdobeRGB() {
+    return {
+        "Adobe RGB (1998)",
+        {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
+        {0.3127f, 0.3290f},
+        2.2f
+    };
+}
+
+const ColorSpace ColorSpace::ProPhotoRGB() {
+    return {
+        "ROMM RGB ISO 22028-2:2013",
+        {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
+        {0.34567f, 0.35850f},
+        {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::DisplayP3() {
+    return {
+        "Display P3",
+        {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
+    };
+}
+
+const ColorSpace ColorSpace::DCIP3() {
+    return {
+        "SMPTE RP 431-2-2007 DCI (P3)",
+        {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+        {0.314f, 0.351f},
+        2.6f
+    };
+}
+
+const ColorSpace ColorSpace::ACES() {
+    return {
+        "SMPTE ST 2065-1:2012 ACES",
+        {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
+        {0.32168f, 0.33767f},
+        1.0f,
+        std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+    };
+}
+
+const ColorSpace ColorSpace::ACEScg() {
+    return {
+        "Academy S-2014-004 ACEScg",
+        {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
+        {0.32168f, 0.33767f},
+        1.0f,
+        std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+    };
+}
+
+std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
+        const ColorSpace& src, const ColorSpace& dst) {
+
+    size = clamp(size, 2u, 256u);
+    float m = 1.0f / float(size - 1);
+
+    std::unique_ptr<float3> lut(new float3[size * size * size]);
+    float3* data = lut.get();
+
+    ColorSpaceConnector connector(src, dst);
+
+    for (uint32_t z = 0; z < size; z++) {
+        for (int32_t y = int32_t(size - 1); y >= 0; y--) {
+            for (uint32_t x = 0; x < size; x++) {
+                *data++ = connector.transform({x * m, y * m, z * m});
+            }
+        }
+    }
+
+    return lut;
+}
+
+static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+    float3{ 0.8951f, -0.7502f,  0.0389f},
+    float3{ 0.2664f,  1.7135f, -0.0685f},
+    float3{-0.1614f,  0.0367f,  1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+    float3 srcLMS = matrix * srcWhitePoint;
+    float3 dstLMS = matrix * dstWhitePoint;
+    return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+ColorSpaceConnector::ColorSpaceConnector(
+        const ColorSpace& src,
+        const ColorSpace& dst) noexcept
+        : mSource(src)
+        , mDestination(dst) {
+
+    if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
+        mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
+    } else {
+        mat3 rgbToXYZ(src.getRGBtoXYZ());
+        mat3 xyzToRGB(dst.getXYZtoRGB());
+
+        float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
+        float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
+
+        if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+            rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
+        }
+
+        if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+            xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
+        }
+
+        mTransform = xyzToRGB * rgbToXYZ;
+    }
+}
+
+}; // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
new file mode 100644
index 0000000..882bd7c
--- /dev/null
+++ b/libs/ui/DebugUtils.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#include <ui/DebugUtils.h>
+
+#include <android-base/stringprintf.h>
+#include <string>
+
+std::string decodeStandard(android_dataspace dataspace) {
+    const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+    switch (dataspaceSelect) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            return std::string("BT709");
+
+        case HAL_DATASPACE_STANDARD_BT601_625:
+            return std::string("BT601_625");
+
+        case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+            return std::string("BT601_625_UNADJUSTED");
+
+        case HAL_DATASPACE_STANDARD_BT601_525:
+            return std::string("BT601_525");
+
+        case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+            return std::string("BT601_525_UNADJUSTED");
+
+        case HAL_DATASPACE_STANDARD_BT2020:
+            return std::string("BT2020");
+
+        case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+            return std::string("BT2020 (constant luminance)");
+
+        case HAL_DATASPACE_STANDARD_BT470M:
+            return std::string("BT470M");
+
+        case HAL_DATASPACE_STANDARD_FILM:
+            return std::string("FILM");
+
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            return std::string("DCI-P3");
+
+        case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+            return std::string("AdobeRGB");
+
+        case 0:
+            switch (dataspace & 0xffff) {
+                case HAL_DATASPACE_JFIF:
+                    return std::string("(deprecated) JFIF (BT601_625, SMPTE_170M Full range)");
+
+                case HAL_DATASPACE_BT601_625:
+                    return std::string("(deprecated) BT601_625 (BT601_625, SMPTE_170M Limited "
+                                       "range)");
+
+                case HAL_DATASPACE_BT601_525:
+                    return std::string("(deprecated) BT601_525 (BT601_525, SMPTE_170M Limited "
+                                       "range)");
+
+                case HAL_DATASPACE_SRGB_LINEAR:
+                    return std::string("(deprecated) SRGB Linear Full range");
+
+                case HAL_DATASPACE_SRGB:
+                    return std::string("(deprecated) sRGB");
+
+                case HAL_DATASPACE_V0_BT709:
+                    return std::string("(deprecated) BT709 (BT709, SMPTE_170M Limited range)");
+
+                case HAL_DATASPACE_ARBITRARY:
+                    return std::string("ARBITRARY");
+
+                case HAL_DATASPACE_UNKNOWN:
+                // Fallthrough
+                default:
+                    return android::base::StringPrintf("Unknown deprecated dataspace code %d",
+                                                       dataspaceSelect);
+            }
+    }
+
+    return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect);
+}
+
+std::string decodeTransfer(android_dataspace dataspace) {
+    const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK);
+    switch (dataspaceTransfer) {
+        case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
+            return std::string("Unspecified");
+
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            return std::string("Linear");
+
+        case HAL_DATASPACE_TRANSFER_SRGB:
+            return std::string("sRGB");
+
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+            return std::string("SMPTE_170M");
+
+        case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+            return std::string("gamma 2.2");
+
+        case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+            return std::string("gamma 2.6");
+
+        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+            return std::string("gamma 2.8");
+
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            return std::string("SMPTE 2084");
+
+        case HAL_DATASPACE_TRANSFER_HLG:
+            return std::string("STD-B67");
+    }
+
+    return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
+}
+
+std::string decodeRange(android_dataspace dataspace) {
+    const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK);
+    switch (dataspaceRange) {
+        case HAL_DATASPACE_RANGE_UNSPECIFIED:
+            return std::string("Range Unspecified");
+
+        case HAL_DATASPACE_RANGE_FULL:
+            return std::string("Full range");
+
+        case HAL_DATASPACE_RANGE_LIMITED:
+            return std::string("Limited range");
+
+        case HAL_DATASPACE_RANGE_EXTENDED:
+            return std::string("Extended range");
+    }
+
+    return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange);
+}
+
+std::string dataspaceDetails(android_dataspace dataspace) {
+    return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
+                                       decodeTransfer(dataspace).c_str(),
+                                       decodeRange(dataspace).c_str());
+}
+
+std::string decodeColorMode(android_color_mode colorMode) {
+    switch (colorMode) {
+        case HAL_COLOR_MODE_NATIVE:
+            return std::string("HAL_COLOR_MODE_NATIVE");
+
+        case HAL_COLOR_MODE_STANDARD_BT601_625:
+            return std::string("HAL_COLOR_MODE_BT601_625");
+
+        case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED:
+            return std::string("HAL_COLOR_MODE_BT601_625_UNADJUSTED");
+
+        case HAL_COLOR_MODE_STANDARD_BT601_525:
+            return std::string("HAL_COLOR_MODE_BT601_525");
+
+        case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED:
+            return std::string("HAL_COLOR_MODE_BT601_525_UNADJUSTED");
+
+        case HAL_COLOR_MODE_STANDARD_BT709:
+            return std::string("HAL_COLOR_MODE_BT709");
+
+        case HAL_COLOR_MODE_DCI_P3:
+            return std::string("HAL_COLOR_MODE_DCI_P3");
+
+        case HAL_COLOR_MODE_SRGB:
+            return std::string("HAL_COLOR_MODE_SRGB");
+
+        case HAL_COLOR_MODE_ADOBE_RGB:
+            return std::string("HAL_COLOR_MODE_ADOBE_RGB");
+
+        case HAL_COLOR_MODE_DISPLAY_P3:
+            return std::string("HAL_COLOR_MODE_DISPLAY_P3");
+    }
+
+    return android::base::StringPrintf("Unknown color mode %d", colorMode);
+}
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 5531b23..b67f4d9 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <ui/Fence.h>
+
 #define LOG_TAG "Fence"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
@@ -25,9 +27,10 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-#include <ui/Fence.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <utils/Log.h>
+#include <utils/String8.h>
 #include <utils/Trace.h>
 
 namespace android {
@@ -109,17 +112,17 @@
 
 nsecs_t Fence::getSignalTime() const {
     if (mFenceFd == -1) {
-        return -1;
+        return SIGNAL_TIME_INVALID;
     }
 
     struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
     if (finfo == NULL) {
         ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
-        return -1;
+        return SIGNAL_TIME_INVALID;
     }
     if (finfo->status != 1) {
         sync_fence_info_free(finfo);
-        return INT64_MAX;
+        return SIGNAL_TIME_PENDING;
     }
 
     struct sync_pt_info* pinfo = NULL;
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
new file mode 100644
index 0000000..1414766
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,367 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <ui/FenceTime.h>
+
+#define LOG_TAG "FenceTime"
+
+#include <cutils/compiler.h>  // For CC_[UN]LIKELY
+#include <utils/Log.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <memory>
+
+namespace android {
+
+// ============================================================================
+// FenceTime
+// ============================================================================
+
+const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
+
+void* FenceTime::operator new(size_t byteCount) noexcept {
+    void *p = nullptr;
+    if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
+        return nullptr;
+    }
+    return p;
+}
+
+void FenceTime::operator delete(void *p) {
+    free(p);
+}
+
+FenceTime::FenceTime(const sp<Fence>& fence)
+  : mState(((fence.get() != nullptr) && fence->isValid()) ?
+            State::VALID : State::INVALID),
+    mFence(fence),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(sp<Fence>&& fence)
+  : mState(((fence.get() != nullptr) && fence->isValid()) ?
+            State::VALID : State::INVALID),
+    mFence(std::move(fence)),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(nsecs_t signalTime)
+  : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
+    mFence(nullptr),
+    mSignalTime(signalTime) {
+    if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) {
+        ALOGE("Pending signal time not allowed after signal.");
+        mSignalTime = Fence::SIGNAL_TIME_INVALID;
+    }
+}
+
+void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
+    if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
+        // Applying Snapshot::State::FENCE, could change the valid state of the
+        // FenceTime, which is not allowed. Callers should create a new
+        // FenceTime from the snapshot instead.
+        ALOGE("applyTrustedSnapshot: Unexpected fence.");
+        return;
+    }
+
+    if (src.state == Snapshot::State::EMPTY) {
+        return;
+    }
+
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        // We should always get the same signalTime here that we did in
+        // getSignalTime(). This check races with getSignalTime(), but it is
+        // only a sanity check so that's okay.
+        if (CC_UNLIKELY(signalTime != src.signalTime)) {
+            ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
+                    "(%" PRId64 " (old) != %" PRId64 " (new))",
+                    signalTime, src.signalTime);
+        }
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mFence.clear();
+    mSignalTime.store(src.signalTime, std::memory_order_relaxed);
+}
+
+bool FenceTime::isValid() const {
+    // We store the valid state in the constructors and return it here.
+    // This lets release code remember the valid state even after the
+    // underlying fence is destroyed.
+    return mState != State::INVALID;
+}
+
+nsecs_t FenceTime::getSignalTime() {
+    // See if we already have a cached value we can return.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return signalTime;
+    }
+
+    // Hold a reference to the fence on the stack in case the class'
+    // reference is removed by another thread. This prevents the
+    // fence from being destroyed until the end of this method, where
+    // we conveniently do not have the lock held.
+    sp<Fence> fence;
+    {
+        // With the lock acquired this time, see if we have the cached
+        // value or if we need to poll the fence.
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (!mFence.get()) {
+            // Another thread set the signal time just before we added the
+            // reference to mFence.
+            return mSignalTime.load(std::memory_order_relaxed);
+        }
+        fence = mFence;
+    }
+
+    // Make the system call without the lock held.
+    signalTime = fence->getSignalTime();
+
+    // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
+    // use invalid underlying Fences without real file descriptors.
+    if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
+        if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+            signalTime = Fence::SIGNAL_TIME_PENDING;
+        }
+    }
+
+    // Make the signal time visible to everyone if it is no longer pending
+    // and remove the class' reference to the fence.
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mFence.clear();
+        mSignalTime.store(signalTime, std::memory_order_relaxed);
+    }
+
+    return signalTime;
+}
+
+nsecs_t FenceTime::getCachedSignalTime() const {
+    // memory_order_acquire since we don't have a lock fallback path
+    // that will do an acquire.
+    return mSignalTime.load(std::memory_order_acquire);
+}
+
+FenceTime::Snapshot FenceTime::getSnapshot() const {
+    // Quick check without the lock.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return Snapshot(signalTime);
+    }
+
+    // Do the full check with the lock.
+    std::lock_guard<std::mutex> lock(mMutex);
+    signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return Snapshot(signalTime);
+    }
+    return Snapshot(mFence);
+}
+
+// For tests only. If forceValidForTest is true, then getSignalTime will
+// never return SIGNAL_TIME_INVALID and isValid will always return true.
+FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
+  : mState(forceValidForTest ?
+            State::FORCED_VALID_FOR_TEST : State::INVALID),
+    mFence(fence),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+void FenceTime::signalForTest(nsecs_t signalTime) {
+    // To be realistic, this should really set a hidden value that
+    // gets picked up in the next call to getSignalTime, but this should
+    // be good enough.
+    std::lock_guard<std::mutex> lock(mMutex);
+    mFence.clear();
+    mSignalTime.store(signalTime, std::memory_order_relaxed);
+}
+
+// ============================================================================
+// FenceTime::Snapshot
+// ============================================================================
+FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
+    : state(State::FENCE), fence(srcFence) {
+}
+
+FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
+    : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
+}
+
+size_t FenceTime::Snapshot::getFlattenedSize() const {
+    constexpr size_t min = sizeof(state);
+    switch (state) {
+        case State::EMPTY:
+            return min;
+        case State::FENCE:
+            return min + fence->getFlattenedSize();
+        case State::SIGNAL_TIME:
+            return min + sizeof(signalTime);
+    }
+    return 0;
+}
+
+size_t FenceTime::Snapshot::getFdCount() const {
+    return state == State::FENCE ? fence->getFdCount() : 0u;
+}
+
+status_t FenceTime::Snapshot::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, state);
+    switch (state) {
+        case State::EMPTY:
+            return NO_ERROR;
+        case State::FENCE:
+            return fence->flatten(buffer, size, fds, count);
+        case State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size, signalTime);
+            return NO_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+status_t FenceTime::Snapshot::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < sizeof(state)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, state);
+    switch (state) {
+        case State::EMPTY:
+            return NO_ERROR;
+        case State::FENCE:
+            fence = new Fence;
+            return fence->unflatten(buffer, size, fds, count);
+        case State::SIGNAL_TIME:
+            if (size < sizeof(signalTime)) {
+                return NO_MEMORY;
+            }
+            FlattenableUtils::read(buffer, size, signalTime);
+            return NO_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+// ============================================================================
+// FenceTimeline
+// ============================================================================
+void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    while (mQueue.size() >= MAX_ENTRIES) {
+        // This is a sanity check to make sure the queue doesn't grow unbounded.
+        // MAX_ENTRIES should be big enough not to trigger this path.
+        // In case this path is taken though, users of FenceTime must make sure
+        // not to rely solely on FenceTimeline to get the final timestamp and
+        // should eventually call Fence::getSignalTime on their own.
+        std::shared_ptr<FenceTime> front = mQueue.front().lock();
+        if (front) {
+            // Make a last ditch effort to get the signalTime here since
+            // we are removing it from the timeline.
+            front->getSignalTime();
+        }
+        mQueue.pop();
+    }
+    mQueue.push(fence);
+}
+
+void FenceTimeline::updateSignalTimes() {
+    while (!mQueue.empty()) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        std::shared_ptr<FenceTime> fence = mQueue.front().lock();
+        if (!fence) {
+            // The shared_ptr no longer exists and no one cares about the
+            // timestamp anymore.
+            mQueue.pop();
+            continue;
+        } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+            // The fence has signaled and we've removed the sp<Fence> ref.
+            mQueue.pop();
+            continue;
+        } else {
+            // The fence didn't signal yet. Break since the later ones
+            // shouldn't have signaled either.
+            break;
+        }
+    }
+}
+
+// ============================================================================
+// FenceToFenceTimeMap
+// ============================================================================
+std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
+        const sp<Fence>& fence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    // Always garbage collecting isn't efficient, but this is only for testing.
+    garbageCollectLocked();
+    std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
+    mMap[fence.get()].push_back(fenceTime);
+    return fenceTime;
+}
+
+void FenceToFenceTimeMap::signalAllForTest(
+        const sp<Fence>& fence, nsecs_t signalTime) {
+    bool signaled = false;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto it = mMap.find(fence.get());
+    if (it != mMap.end()) {
+        for (auto& weakFenceTime : it->second) {
+            std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
+            if (!fenceTime) {
+                continue;
+            }
+            ALOGE_IF(!fenceTime->isValid(),
+                    "signalAllForTest: Signaling invalid fence.");
+            fenceTime->signalForTest(signalTime);
+            signaled = true;
+        }
+    }
+
+    ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal.");
+}
+
+void FenceToFenceTimeMap::garbageCollectLocked() {
+    for (auto& it : mMap) {
+        // Erase all expired weak pointers from the vector.
+        auto& vect = it.second;
+        vect.erase(
+                std::remove_if(vect.begin(), vect.end(),
+                        [](const std::weak_ptr<FenceTime>& ft) {
+                            return ft.expired();
+                        }),
+                vect.end());
+
+        // Also erase the map entry if the vector is now empty.
+        if (vect.empty()) {
+            mMap.erase(it.first);
+        }
+    }
+}
+
+} // namespace android
diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp
deleted file mode 100644
index 4c73ce4..0000000
--- a/libs/ui/Gralloc1.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-
-#include <ui/Gralloc1.h>
-
-#include <vector>
-
-#undef LOG_TAG
-#define LOG_TAG GRALLOC1_LOG_TAG
-
-namespace android {
-
-namespace Gralloc1 {
-
-Descriptor::~Descriptor()
-{
-    int32_t intError = mShimDevice.mFunctions.destroyDescriptor(
-            mShimDevice.mDevice, mDeviceId);
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("destroyDescriptor failed: %d", intError);
-    }
-}
-
-gralloc1_error_t Descriptor::setDimensions(uint32_t width, uint32_t height)
-{
-    int32_t intError = mShimDevice.mFunctions.setDimensions(mShimDevice.mDevice,
-            mDeviceId, width, height);
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error != GRALLOC1_ERROR_NONE) {
-        return error;
-    }
-    mWidth = width;
-    mHeight = height;
-    return error;
-}
-
-template <typename ApiType>
-struct Setter {
-    typedef int32_t (*Type)(gralloc1_device_t*, gralloc1_buffer_descriptor_t,
-            ApiType);
-};
-
-template <typename ApiType, typename ValueType>
-static inline gralloc1_error_t setHelper(
-        typename Setter<ApiType>::Type setter, gralloc1_device_t* device,
-        gralloc1_buffer_descriptor_t id, ValueType newValue,
-        ValueType* cacheVariable)
-{
-    int32_t intError = setter(device, id, static_cast<ApiType>(newValue));
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error != GRALLOC1_ERROR_NONE) {
-        return error;
-    }
-    *cacheVariable = newValue;
-    return error;
-}
-
-gralloc1_error_t Descriptor::setFormat(android_pixel_format_t format)
-{
-    return setHelper<int32_t>(mShimDevice.mFunctions.setFormat.pfn,
-            mShimDevice.mDevice, mDeviceId, format, &mFormat);
-}
-
-gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage)
-{
-    return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn,
-            mShimDevice.mDevice, mDeviceId, usage, &mProducerUsage);
-}
-
-gralloc1_error_t Descriptor::setConsumerUsage(gralloc1_consumer_usage_t usage)
-{
-    return setHelper<uint64_t>(mShimDevice.mFunctions.setConsumerUsage.pfn,
-            mShimDevice.mDevice, mDeviceId, usage, &mConsumerUsage);
-}
-
-Device::Device(gralloc1_device_t* device)
-  : mDevice(device),
-    mCapabilities(loadCapabilities()),
-    mFunctions()
-{
-    if (!loadFunctions()) {
-        ALOGE("Failed to load a required function, aborting");
-        abort();
-    }
-}
-
-bool Device::hasCapability(gralloc1_capability_t capability) const
-{
-    return mCapabilities.count(capability) > 0;
-}
-
-std::string Device::dump()
-{
-    uint32_t length = 0;
-    mFunctions.dump(mDevice, &length, nullptr);
-
-    std::vector<char> output;
-    output.resize(length);
-    mFunctions.dump(mDevice, &length, output.data());
-
-    return std::string(output.cbegin(), output.cend());
-}
-
-std::shared_ptr<Descriptor> Device::createDescriptor()
-{
-    gralloc1_buffer_descriptor_t descriptorId;
-    int32_t intError = mFunctions.createDescriptor(mDevice, &descriptorId);
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error != GRALLOC1_ERROR_NONE) {
-        return nullptr;
-    }
-    auto descriptor = std::make_shared<Descriptor>(*this, descriptorId);
-    return descriptor;
-}
-
-gralloc1_error_t Device::getStride(buffer_handle_t buffer, uint32_t* outStride)
-{
-    int32_t intError = mFunctions.getStride(mDevice, buffer, outStride);
-    return static_cast<gralloc1_error_t>(intError);
-}
-
-static inline bool allocationSucceded(gralloc1_error_t error)
-{
-    return error == GRALLOC1_ERROR_NONE || error == GRALLOC1_ERROR_NOT_SHARED;
-}
-
-gralloc1_error_t Device::allocate(
-        const std::vector<std::shared_ptr<const Descriptor>>& descriptors,
-        std::vector<buffer_handle_t>* outBuffers)
-{
-    if (mFunctions.allocate.pfn == nullptr) {
-        // Allocation is not supported on this device
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    std::vector<gralloc1_buffer_descriptor_t> deviceIds;
-    for (const auto& descriptor : descriptors) {
-        deviceIds.emplace_back(descriptor->getDeviceId());
-    }
-
-    std::vector<buffer_handle_t> buffers(descriptors.size());
-    int32_t intError = mFunctions.allocate(mDevice,
-            static_cast<uint32_t>(descriptors.size()), deviceIds.data(),
-            buffers.data());
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (allocationSucceded(error)) {
-        *outBuffers = std::move(buffers);
-    }
-
-    return error;
-}
-
-gralloc1_error_t Device::allocate(
-        const std::shared_ptr<const Descriptor>& descriptor,
-        gralloc1_backing_store_t id, buffer_handle_t* outBuffer)
-{
-    gralloc1_error_t error = GRALLOC1_ERROR_NONE;
-
-    if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
-        buffer_handle_t buffer = nullptr;
-        int32_t intError = mFunctions.allocateWithId(mDevice,
-                descriptor->getDeviceId(), id, &buffer);
-        error = static_cast<gralloc1_error_t>(intError);
-        if (allocationSucceded(error)) {
-            *outBuffer = buffer;
-        }
-    } else {
-        std::vector<std::shared_ptr<const Descriptor>> descriptors;
-        descriptors.emplace_back(descriptor);
-        std::vector<buffer_handle_t> buffers;
-        error = allocate(descriptors, &buffers);
-        if (allocationSucceded(error)) {
-            *outBuffer = buffers[0];
-        }
-    }
-
-    return error;
-}
-
-gralloc1_error_t Device::retain(buffer_handle_t buffer)
-{
-    int32_t intError = mFunctions.retain(mDevice, buffer);
-    return static_cast<gralloc1_error_t>(intError);
-}
-
-gralloc1_error_t Device::retain(const GraphicBuffer* buffer)
-{
-    if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
-        return mFunctions.retainGraphicBuffer(mDevice, buffer);
-    } else {
-        return retain(buffer->getNativeBuffer()->handle);
-    }
-}
-
-gralloc1_error_t Device::release(buffer_handle_t buffer)
-{
-    int32_t intError = mFunctions.release(mDevice, buffer);
-    return static_cast<gralloc1_error_t>(intError);
-}
-
-gralloc1_error_t Device::getNumFlexPlanes(buffer_handle_t buffer,
-        uint32_t* outNumPlanes)
-{
-    uint32_t numPlanes = 0;
-    int32_t intError = mFunctions.getNumFlexPlanes(mDevice, buffer, &numPlanes);
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error == GRALLOC1_ERROR_NONE) {
-        *outNumPlanes = numPlanes;
-    }
-    return error;
-}
-
-gralloc1_error_t Device::lock(buffer_handle_t buffer,
-        gralloc1_producer_usage_t producerUsage,
-        gralloc1_consumer_usage_t consumerUsage,
-        const gralloc1_rect_t* accessRegion, void** outData,
-        const sp<Fence>& acquireFence)
-{
-    ALOGV("Calling lock(%p)", buffer);
-    return lockHelper(mFunctions.lock.pfn, buffer, producerUsage,
-            consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::lockFlex(buffer_handle_t buffer,
-        gralloc1_producer_usage_t producerUsage,
-        gralloc1_consumer_usage_t consumerUsage,
-        const gralloc1_rect_t* accessRegion,
-        struct android_flex_layout* outData,
-        const sp<Fence>& acquireFence)
-{
-    ALOGV("Calling lockFlex(%p)", buffer);
-    return lockHelper(mFunctions.lockFlex.pfn, buffer, producerUsage,
-            consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::lockYCbCr(buffer_handle_t buffer,
-        gralloc1_producer_usage_t producerUsage,
-        gralloc1_consumer_usage_t consumerUsage,
-        const gralloc1_rect_t* accessRegion,
-        struct android_ycbcr* outData,
-        const sp<Fence>& acquireFence)
-{
-    ALOGV("Calling lockYCbCr(%p)", buffer);
-    return lockHelper(mFunctions.lockYCbCr.pfn, buffer, producerUsage,
-            consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::unlock(buffer_handle_t buffer, sp<Fence>* outFence)
-{
-    int32_t fenceFd = -1;
-    int32_t intError = mFunctions.unlock(mDevice, buffer, &fenceFd);
-    auto error = static_cast<gralloc1_error_t>(intError);
-    if (error == GRALLOC1_ERROR_NONE) {
-        *outFence = new Fence(fenceFd);
-    }
-    return error;
-}
-
-std::unordered_set<gralloc1_capability_t> Device::loadCapabilities()
-{
-    std::vector<int32_t> intCapabilities;
-    uint32_t numCapabilities = 0;
-    mDevice->getCapabilities(mDevice, &numCapabilities, nullptr);
-
-    intCapabilities.resize(numCapabilities);
-    mDevice->getCapabilities(mDevice, &numCapabilities, intCapabilities.data());
-
-    std::unordered_set<gralloc1_capability_t> capabilities;
-    for (const auto intCapability : intCapabilities) {
-        capabilities.emplace(static_cast<gralloc1_capability_t>(intCapability));
-    }
-    return capabilities;
-}
-
-bool Device::loadFunctions()
-{
-    // Functions which must always be present
-    if (!mFunctions.dump.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.createDescriptor.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.destroyDescriptor.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.setConsumerUsage.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.setDimensions.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.setFormat.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.setProducerUsage.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getBackingStore.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getConsumerUsage.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getDimensions.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getFormat.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getProducerUsage.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getStride.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.retain.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.release.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.getNumFlexPlanes.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.lock.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.lockFlex.load(mDevice, true)) {
-        return false;
-    }
-    if (!mFunctions.unlock.load(mDevice, true)) {
-        return false;
-    }
-
-    if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
-        // These should always be present on the adapter
-        if (!mFunctions.retainGraphicBuffer.load(mDevice, true)) {
-            return false;
-        }
-        if (!mFunctions.lockYCbCr.load(mDevice, true)) {
-            return false;
-        }
-
-        // allocateWithId may not be present if we're only able to map in this
-        // process
-        mFunctions.allocateWithId.load(mDevice, false);
-    } else {
-        // allocate may not be present if we're only able to map in this process
-        mFunctions.allocate.load(mDevice, false);
-    }
-
-    return true;
-}
-
-std::unique_ptr<Gralloc1On0Adapter> Loader::mAdapter = nullptr;
-
-Loader::Loader()
-  : mDevice(nullptr)
-{
-    hw_module_t const* module;
-    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
-    uint8_t majorVersion = (module->module_api_version >> 8) & 0xFF;
-    uint8_t minorVersion = module->module_api_version & 0xFF;
-    gralloc1_device_t* device = nullptr;
-    if (majorVersion == 1) {
-        gralloc1_open(module, &device);
-    } else {
-        if (!mAdapter) {
-            mAdapter = std::make_unique<Gralloc1On0Adapter>(module);
-        }
-        device = mAdapter->getDevice();
-    }
-    mDevice = std::make_unique<Gralloc1::Device>(device);
-}
-
-Loader::~Loader() {}
-
-std::unique_ptr<Device> Loader::getDevice()
-{
-    return std::move(mDevice);
-}
-
-} // namespace android::Gralloc1
-
-} // namespace android
diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
deleted file mode 100644
index ec7df31..0000000
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Gralloc1On0Adapter"
-//#define LOG_NDEBUG 0
-
-#include <hardware/gralloc.h>
-
-#include <ui/Gralloc1On0Adapter.h>
-
-#include <utils/Log.h>
-
-#include <inttypes.h>
-
-template <typename PFN, typename T>
-static gralloc1_function_pointer_t asFP(T function)
-{
-    static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
-    return reinterpret_cast<gralloc1_function_pointer_t>(function);
-}
-
-namespace android {
-
-Gralloc1On0Adapter::Gralloc1On0Adapter(const hw_module_t* module)
-  : mModule(reinterpret_cast<const gralloc_module_t*>(module)),
-    mMinorVersion(mModule->common.module_api_version & 0xFF),
-    mDevice(nullptr)
-{
-    ALOGV("Constructing");
-    getCapabilities = getCapabilitiesHook;
-    getFunction = getFunctionHook;
-    int error = ::gralloc_open(&(mModule->common), &mDevice);
-    if (error) {
-        ALOGE("Failed to open gralloc0 module: %d", error);
-    }
-    ALOGV("Opened gralloc0 device %p", mDevice);
-}
-
-Gralloc1On0Adapter::~Gralloc1On0Adapter()
-{
-    ALOGV("Destructing");
-    if (mDevice) {
-        ALOGV("Closing gralloc0 device %p", mDevice);
-        ::gralloc_close(mDevice);
-    }
-}
-
-void Gralloc1On0Adapter::doGetCapabilities(uint32_t* outCount,
-        int32_t* outCapabilities)
-{
-    if (outCapabilities == nullptr) {
-        *outCount = 1;
-        return;
-    }
-    if (*outCount >= 1) {
-        *outCapabilities = GRALLOC1_CAPABILITY_ON_ADAPTER;
-        *outCount = 1;
-    }
-}
-
-gralloc1_function_pointer_t Gralloc1On0Adapter::doGetFunction(
-        int32_t intDescriptor)
-{
-    constexpr auto lastDescriptor =
-            static_cast<int32_t>(GRALLOC1_LAST_ADAPTER_FUNCTION);
-    if (intDescriptor < 0 || intDescriptor > lastDescriptor) {
-        ALOGE("Invalid function descriptor");
-        return nullptr;
-    }
-
-    auto descriptor =
-            static_cast<gralloc1_function_descriptor_t>(intDescriptor);
-    switch (descriptor) {
-        case GRALLOC1_FUNCTION_DUMP:
-            return asFP<GRALLOC1_PFN_DUMP>(dumpHook);
-        case GRALLOC1_FUNCTION_CREATE_DESCRIPTOR:
-            return asFP<GRALLOC1_PFN_CREATE_DESCRIPTOR>(createDescriptorHook);
-        case GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR:
-            return asFP<GRALLOC1_PFN_DESTROY_DESCRIPTOR>(destroyDescriptorHook);
-        case GRALLOC1_FUNCTION_SET_CONSUMER_USAGE:
-            return asFP<GRALLOC1_PFN_SET_CONSUMER_USAGE>(setConsumerUsageHook);
-        case GRALLOC1_FUNCTION_SET_DIMENSIONS:
-            return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook);
-        case GRALLOC1_FUNCTION_SET_FORMAT:
-            return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook);
-        case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
-            return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook);
-        case GRALLOC1_FUNCTION_GET_BACKING_STORE:
-            return asFP<GRALLOC1_PFN_GET_BACKING_STORE>(
-                    bufferHook<decltype(&Buffer::getBackingStore),
-                    &Buffer::getBackingStore, gralloc1_backing_store_t*>);
-        case GRALLOC1_FUNCTION_GET_CONSUMER_USAGE:
-            return asFP<GRALLOC1_PFN_GET_CONSUMER_USAGE>(getConsumerUsageHook);
-        case GRALLOC1_FUNCTION_GET_DIMENSIONS:
-            return asFP<GRALLOC1_PFN_GET_DIMENSIONS>(
-                    bufferHook<decltype(&Buffer::getDimensions),
-                    &Buffer::getDimensions, uint32_t*, uint32_t*>);
-        case GRALLOC1_FUNCTION_GET_FORMAT:
-            return asFP<GRALLOC1_PFN_GET_FORMAT>(
-                    bufferHook<decltype(&Buffer::getFormat),
-                    &Buffer::getFormat, int32_t*>);
-        case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
-            return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook);
-        case GRALLOC1_FUNCTION_GET_STRIDE:
-            return asFP<GRALLOC1_PFN_GET_STRIDE>(
-                    bufferHook<decltype(&Buffer::getStride),
-                    &Buffer::getStride, uint32_t*>);
-        case GRALLOC1_FUNCTION_ALLOCATE:
-            // Not provided, since we'll use ALLOCATE_WITH_ID
-            return nullptr;
-        case GRALLOC1_FUNCTION_ALLOCATE_WITH_ID:
-            if (mDevice != nullptr) {
-                return asFP<GRALLOC1_PFN_ALLOCATE_WITH_ID>(allocateWithIdHook);
-            } else {
-                return nullptr;
-            }
-        case GRALLOC1_FUNCTION_RETAIN:
-            return asFP<GRALLOC1_PFN_RETAIN>(
-                    managementHook<&Gralloc1On0Adapter::retain>);
-        case GRALLOC1_FUNCTION_RELEASE:
-            return asFP<GRALLOC1_PFN_RELEASE>(
-                    managementHook<&Gralloc1On0Adapter::release>);
-        case GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER:
-            return asFP<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER>(
-                    retainGraphicBufferHook);
-        case GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES:
-            return asFP<GRALLOC1_PFN_GET_NUM_FLEX_PLANES>(
-                    bufferHook<decltype(&Buffer::getNumFlexPlanes),
-                    &Buffer::getNumFlexPlanes, uint32_t*>);
-        case GRALLOC1_FUNCTION_LOCK:
-            return asFP<GRALLOC1_PFN_LOCK>(
-                    lockHook<void*, &Gralloc1On0Adapter::lock>);
-        case GRALLOC1_FUNCTION_LOCK_FLEX:
-            return asFP<GRALLOC1_PFN_LOCK_FLEX>(
-                    lockHook<struct android_flex_layout,
-                    &Gralloc1On0Adapter::lockFlex>);
-        case GRALLOC1_FUNCTION_LOCK_YCBCR:
-            return asFP<GRALLOC1_PFN_LOCK_YCBCR>(
-                    lockHook<struct android_ycbcr,
-                    &Gralloc1On0Adapter::lockYCbCr>);
-        case GRALLOC1_FUNCTION_UNLOCK:
-            return asFP<GRALLOC1_PFN_UNLOCK>(unlockHook);
-        case GRALLOC1_FUNCTION_INVALID:
-            ALOGE("Invalid function descriptor");
-            return nullptr;
-    }
-
-    ALOGE("Unknown function descriptor: %d", intDescriptor);
-    return nullptr;
-}
-
-void Gralloc1On0Adapter::dump(uint32_t* outSize, char* outBuffer)
-{
-    ALOGV("dump(%u (%p), %p", outSize ? *outSize : 0, outSize, outBuffer);
-
-    if (!mDevice->dump) {
-        // dump is optional on gralloc0 implementations
-        *outSize = 0;
-        return;
-    }
-
-    if (!outBuffer) {
-        constexpr int32_t BUFFER_LENGTH = 4096;
-        char buffer[BUFFER_LENGTH] = {};
-        mDevice->dump(mDevice, buffer, BUFFER_LENGTH);
-        buffer[BUFFER_LENGTH - 1] = 0; // Ensure the buffer is null-terminated
-        size_t actualLength = std::strlen(buffer);
-        mCachedDump.resize(actualLength);
-        std::copy_n(buffer, actualLength, mCachedDump.begin());
-        *outSize = static_cast<uint32_t>(actualLength);
-    } else {
-        *outSize = std::min(*outSize,
-                static_cast<uint32_t>(mCachedDump.size()));
-        outBuffer = std::copy_n(mCachedDump.cbegin(), *outSize, outBuffer);
-    }
-}
-
-gralloc1_error_t Gralloc1On0Adapter::createDescriptor(
-        gralloc1_buffer_descriptor_t* outDescriptor)
-{
-    auto descriptorId = sNextBufferDescriptorId++;
-    std::lock_guard<std::mutex> lock(mDescriptorMutex);
-    mDescriptors.emplace(descriptorId,
-            std::make_shared<Descriptor>(this, descriptorId));
-
-    ALOGV("Created descriptor %" PRIu64, descriptorId);
-
-    *outDescriptor = descriptorId;
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::destroyDescriptor(
-        gralloc1_buffer_descriptor_t descriptor)
-{
-    ALOGV("Destroying descriptor %" PRIu64, descriptor);
-
-    std::lock_guard<std::mutex> lock(mDescriptorMutex);
-    if (mDescriptors.count(descriptor) == 0) {
-        return GRALLOC1_ERROR_BAD_DESCRIPTOR;
-    }
-
-    mDescriptors.erase(descriptor);
-    return GRALLOC1_ERROR_NONE;
-}
-
-Gralloc1On0Adapter::Buffer::Buffer(buffer_handle_t handle,
-        gralloc1_backing_store_t store, const Descriptor& descriptor,
-        uint32_t stride, bool wasAllocated)
-  : mHandle(handle),
-    mReferenceCount(1),
-    mStore(store),
-    mDescriptor(descriptor),
-    mStride(stride),
-    mWasAllocated(wasAllocated) {}
-
-gralloc1_error_t Gralloc1On0Adapter::allocate(
-        const std::shared_ptr<Descriptor>& descriptor,
-        gralloc1_backing_store_t store,
-        buffer_handle_t* outBufferHandle)
-{
-    ALOGV("allocate(%" PRIu64 ", %#" PRIx64 ")", descriptor->id, store);
-
-    // If this function is being called, it's because we handed out its function
-    // pointer, which only occurs when mDevice has been loaded successfully and
-    // we are permitted to allocate
-
-    int usage = static_cast<int>(descriptor->producerUsage) |
-            static_cast<int>(descriptor->consumerUsage);
-    buffer_handle_t handle = nullptr;
-    int stride = 0;
-    ALOGV("Calling alloc(%p, %u, %u, %i, %u)", mDevice, descriptor->width,
-            descriptor->height, descriptor->format, usage);
-    auto error = mDevice->alloc(mDevice,
-            static_cast<int>(descriptor->width),
-            static_cast<int>(descriptor->height), descriptor->format,
-            usage, &handle, &stride);
-    if (error != 0) {
-        ALOGE("gralloc0 allocation failed: %d (%s)", error,
-                strerror(-error));
-        return GRALLOC1_ERROR_NO_RESOURCES;
-    }
-
-    *outBufferHandle = handle;
-    auto buffer = std::make_shared<Buffer>(handle, store, *descriptor, stride,
-            true);
-
-    std::lock_guard<std::mutex> lock(mBufferMutex);
-    mBuffers.emplace(handle, std::move(buffer));
-
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::allocateWithIdHook(
-        gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptorId,
-        gralloc1_backing_store_t store, buffer_handle_t* outBuffer)
-{
-    auto adapter = getAdapter(device);
-
-    auto descriptor = adapter->getDescriptor(descriptorId);
-    if (!descriptor) {
-        return GRALLOC1_ERROR_BAD_DESCRIPTOR;
-    }
-
-    buffer_handle_t bufferHandle = nullptr;
-    auto error = adapter->allocate(descriptor, store, &bufferHandle);
-    if (error != GRALLOC1_ERROR_NONE) {
-        return error;
-    }
-
-    *outBuffer = bufferHandle;
-    return error;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::retain(
-        const std::shared_ptr<Buffer>& buffer)
-{
-    std::lock_guard<std::mutex> lock(mBufferMutex);
-    buffer->retain();
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::release(
-        const std::shared_ptr<Buffer>& buffer)
-{
-    std::lock_guard<std::mutex> lock(mBufferMutex);
-    if (!buffer->release()) {
-        return GRALLOC1_ERROR_NONE;
-    }
-
-    buffer_handle_t handle = buffer->getHandle();
-    if (buffer->wasAllocated()) {
-        ALOGV("Calling free(%p)", handle);
-        int result = mDevice->free(mDevice, handle);
-        if (result != 0) {
-            ALOGE("gralloc0 free failed: %d", result);
-        }
-    } else {
-        ALOGV("Calling unregisterBuffer(%p)", handle);
-        int result = mModule->unregisterBuffer(mModule, handle);
-        if (result != 0) {
-            ALOGE("gralloc0 unregister failed: %d", result);
-        }
-    }
-
-    mBuffers.erase(handle);
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::retain(
-        const android::GraphicBuffer* graphicBuffer)
-{
-    ALOGV("retainGraphicBuffer(%p, %#" PRIx64 ")",
-            graphicBuffer->getNativeBuffer()->handle, graphicBuffer->getId());
-
-    buffer_handle_t handle = graphicBuffer->getNativeBuffer()->handle;
-    std::lock_guard<std::mutex> lock(mBufferMutex);
-    if (mBuffers.count(handle) != 0) {
-        mBuffers[handle]->retain();
-        return GRALLOC1_ERROR_NONE;
-    }
-
-    ALOGV("Calling registerBuffer(%p)", handle);
-    int result = mModule->registerBuffer(mModule, handle);
-    if (result != 0) {
-        ALOGE("gralloc0 register failed: %d", result);
-        return GRALLOC1_ERROR_NO_RESOURCES;
-    }
-
-    Descriptor descriptor{this, sNextBufferDescriptorId++};
-    descriptor.setDimensions(graphicBuffer->getWidth(),
-            graphicBuffer->getHeight());
-    descriptor.setFormat(graphicBuffer->getPixelFormat());
-    descriptor.setProducerUsage(
-            static_cast<gralloc1_producer_usage_t>(graphicBuffer->getUsage()));
-    descriptor.setConsumerUsage(
-            static_cast<gralloc1_consumer_usage_t>(graphicBuffer->getUsage()));
-    auto buffer = std::make_shared<Buffer>(handle,
-            static_cast<gralloc1_backing_store_t>(graphicBuffer->getId()),
-            descriptor, graphicBuffer->getStride(), false);
-    mBuffers.emplace(handle, std::move(buffer));
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lock(
-        const std::shared_ptr<Buffer>& buffer,
-        gralloc1_producer_usage_t producerUsage,
-        gralloc1_consumer_usage_t consumerUsage,
-        const gralloc1_rect_t& accessRegion, void** outData,
-        const sp<Fence>& acquireFence)
-{
-    if (mMinorVersion >= 3) {
-        int result = mModule->lockAsync(mModule, buffer->getHandle(),
-                static_cast<int32_t>(producerUsage | consumerUsage),
-                accessRegion.left, accessRegion.top, accessRegion.width,
-                accessRegion.height, outData, acquireFence->dup());
-        if (result != 0) {
-            return GRALLOC1_ERROR_UNSUPPORTED;
-        }
-    } else {
-        acquireFence->waitForever("Gralloc1On0Adapter::lock");
-        int result = mModule->lock(mModule, buffer->getHandle(),
-                static_cast<int32_t>(producerUsage | consumerUsage),
-                accessRegion.left, accessRegion.top, accessRegion.width,
-                accessRegion.height, outData);
-        ALOGV("gralloc0 lock returned %d", result);
-        if (result != 0) {
-            return GRALLOC1_ERROR_UNSUPPORTED;
-        }
-    }
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lockFlex(
-        const std::shared_ptr<Buffer>& /*buffer*/,
-        gralloc1_producer_usage_t /*producerUsage*/,
-        gralloc1_consumer_usage_t /*consumerUsage*/,
-        const gralloc1_rect_t& /*accessRegion*/,
-        struct android_flex_layout* /*outData*/,
-        const sp<Fence>& /*acquireFence*/)
-{
-    // TODO
-    return GRALLOC1_ERROR_UNSUPPORTED;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lockYCbCr(
-        const std::shared_ptr<Buffer>& buffer,
-        gralloc1_producer_usage_t producerUsage,
-        gralloc1_consumer_usage_t consumerUsage,
-        const gralloc1_rect_t& accessRegion, struct android_ycbcr* outData,
-        const sp<Fence>& acquireFence)
-{
-    if (mMinorVersion >= 3 && mModule->lockAsync_ycbcr) {
-        int result = mModule->lockAsync_ycbcr(mModule, buffer->getHandle(),
-                static_cast<int>(producerUsage | consumerUsage),
-                accessRegion.left, accessRegion.top, accessRegion.width,
-                accessRegion.height, outData, acquireFence->dup());
-        if (result != 0) {
-            return GRALLOC1_ERROR_UNSUPPORTED;
-        }
-    } else if (mModule->lock_ycbcr) {
-        acquireFence->waitForever("Gralloc1On0Adapter::lockYCbCr");
-        int result = mModule->lock_ycbcr(mModule, buffer->getHandle(),
-                static_cast<int>(producerUsage | consumerUsage),
-                accessRegion.left, accessRegion.top, accessRegion.width,
-                accessRegion.height, outData);
-        ALOGV("gralloc0 lockYCbCr returned %d", result);
-        if (result != 0) {
-            return GRALLOC1_ERROR_UNSUPPORTED;
-        }
-    } else {
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::unlock(
-        const std::shared_ptr<Buffer>& buffer,
-        sp<Fence>* outReleaseFence)
-{
-    if (mMinorVersion >= 3) {
-        int fenceFd = -1;
-        int result = mModule->unlockAsync(mModule, buffer->getHandle(),
-                &fenceFd);
-        if (result != 0) {
-            close(fenceFd);
-            ALOGE("gralloc0 unlockAsync failed: %d", result);
-        } else {
-            *outReleaseFence = new Fence(fenceFd);
-        }
-    } else {
-        int result = mModule->unlock(mModule, buffer->getHandle());
-        if (result != 0) {
-            ALOGE("gralloc0 unlock failed: %d", result);
-        }
-    }
-    return GRALLOC1_ERROR_NONE;
-}
-
-std::shared_ptr<Gralloc1On0Adapter::Descriptor>
-Gralloc1On0Adapter::getDescriptor(gralloc1_buffer_descriptor_t descriptorId)
-{
-    std::lock_guard<std::mutex> lock(mDescriptorMutex);
-    if (mDescriptors.count(descriptorId) == 0) {
-        return nullptr;
-    }
-
-    return mDescriptors[descriptorId];
-}
-
-std::shared_ptr<Gralloc1On0Adapter::Buffer> Gralloc1On0Adapter::getBuffer(
-        buffer_handle_t bufferHandle)
-{
-    std::lock_guard<std::mutex> lock(mBufferMutex);
-    if (mBuffers.count(bufferHandle) == 0) {
-        return nullptr;
-    }
-
-    return mBuffers[bufferHandle];
-}
-
-std::atomic<gralloc1_buffer_descriptor_t>
-        Gralloc1On0Adapter::sNextBufferDescriptorId(1);
-
-} // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
new file mode 100644
index 0000000..87dbaf4
--- /dev/null
+++ b/libs/ui/Gralloc2.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Gralloc2"
+
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc2.h>
+
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+namespace android {
+
+namespace Gralloc2 {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+Mapper::Mapper()
+{
+    mMapper = IMapper::getService();
+    if (mMapper == nullptr || mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+Error Mapper::createDescriptor(
+        const IMapper::BufferDescriptorInfo& descriptorInfo,
+        BufferDescriptor* outDescriptor) const
+{
+    Error error;
+    auto ret = mMapper->createDescriptor(descriptorInfo,
+            [&](const auto& tmpError, const auto& tmpDescriptor)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outDescriptor = tmpDescriptor;
+            });
+
+    return (ret.isOk()) ? error : kTransactionError;
+}
+
+Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+        buffer_handle_t* outBufferHandle) const
+{
+    Error error;
+    auto ret = mMapper->importBuffer(rawHandle,
+            [&](const auto& tmpError, const auto& tmpBuffer)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+            });
+
+    return (ret.isOk()) ? error : kTransactionError;
+}
+
+void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
+{
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->freeBuffer(buffer);
+
+    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d",
+            buffer, error);
+}
+
+Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
+        const IMapper::Rect& accessRegion,
+        int acquireFence, void** outData) const
+{
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    Error error;
+    auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+            [&](const auto& tmpError, const auto& tmpData)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outData = tmpData;
+            });
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return (ret.isOk()) ? error : kTransactionError;
+}
+
+Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
+        const IMapper::Rect& accessRegion,
+        int acquireFence, YCbCrLayout* outLayout) const
+{
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    Error error;
+    auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
+            acquireFenceHandle,
+            [&](const auto& tmpError, const auto& tmpLayout)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outLayout = tmpLayout;
+            });
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return (ret.isOk()) ? error : kTransactionError;
+}
+
+int Mapper::unlock(buffer_handle_t bufferHandle) const
+{
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    int releaseFence = -1;
+    Error error;
+    auto ret = mMapper->unlock(buffer,
+            [&](const auto& tmpError, const auto& tmpReleaseFence)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                auto fenceHandle = tmpReleaseFence.getNativeHandle();
+                if (fenceHandle && fenceHandle->numFds == 1) {
+                    int fd = dup(fenceHandle->data[0]);
+                    if (fd >= 0) {
+                        releaseFence = fd;
+                    } else {
+                        ALOGD("failed to dup unlock release fence");
+                        sync_wait(fenceHandle->data[0], -1);
+                    }
+                }
+            });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("unlock(%p) failed with %d", buffer, error);
+    }
+
+    return releaseFence;
+}
+
+Allocator::Allocator(const Mapper& mapper)
+    : mMapper(mapper)
+{
+    mAllocator = IAllocator::getService();
+    if (mAllocator == nullptr) {
+        LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+    }
+}
+
+std::string Allocator::dumpDebugInfo() const
+{
+    std::string debugInfo;
+
+    mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
+        debugInfo = tmpDebugInfo.c_str();
+    });
+
+    return debugInfo;
+}
+
+Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
+        uint32_t* outStride, buffer_handle_t* outBufferHandles) const
+{
+    Error error;
+    auto ret = mAllocator->allocate(descriptor, count,
+            [&](const auto& tmpError, const auto& tmpStride,
+                const auto& tmpBuffers) {
+                error = tmpError;
+                if (tmpError != Error::NONE) {
+                    return;
+                }
+
+                // import buffers
+                for (uint32_t i = 0; i < count; i++) {
+                    error = mMapper.importBuffer(tmpBuffers[i],
+                            &outBufferHandles[i]);
+                    if (error != Error::NONE) {
+                        for (uint32_t j = 0; j < i; j++) {
+                            mMapper.freeBuffer(outBufferHandles[j]);
+                            outBufferHandles[j] = nullptr;
+                        }
+                        return;
+                    }
+                }
+
+                *outStride = tmpStride;
+            });
+
+    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+    hardware::IPCThreadState::self()->flushCommands();
+
+    return (ret.isOk()) ? error : kTransactionError;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 97b948d..ee85c9b 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -16,17 +16,15 @@
 
 #define LOG_TAG "GraphicBuffer"
 
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
 #include <ui/GraphicBuffer.h>
+
+#include <cutils/atomic.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
+
+#include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
-#include <ui/PixelFormat.h>
 
 namespace android {
 
@@ -41,6 +39,10 @@
     return id;
 }
 
+sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) {
+    return static_cast<GraphicBuffer *>(anwb);
+}
+
 GraphicBuffer::GraphicBuffer()
     : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
       mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -50,51 +52,46 @@
     stride =
     format =
     usage  = 0;
+    layerCount = 0;
     handle = NULL;
 }
 
+// deprecated
 GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
         PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
-    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
-      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+    : GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast<uint64_t>(inUsage),
+            requestorName)
 {
-    width  =
-    height =
-    stride =
-    format =
-    usage  = 0;
-    handle = NULL;
-    mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
-            std::move(requestorName));
 }
 
 GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage, uint32_t inStride,
-        native_handle_t* inHandle, bool keepOwnership)
-    : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
-      mBufferMapper(GraphicBufferMapper::get()),
-      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+        PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage,
+        std::string requestorName)
+    : GraphicBuffer()
 {
-    width  = static_cast<int>(inWidth);
-    height = static_cast<int>(inHeight);
-    stride = static_cast<int>(inStride);
-    format = inFormat;
-    usage  = static_cast<int>(inUsage);
-    handle = inHandle;
+    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
+            usage, std::move(requestorName));
 }
 
-GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)
-    : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
-      mBufferMapper(GraphicBufferMapper::get()),
-      mInitCheck(NO_ERROR), mWrappedBuffer(buffer), mId(getUniqueId()),
-      mGenerationNumber(0)
+// deprecated
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+        uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
+    : GraphicBuffer(inHandle, keepOwnership ? TAKE_HANDLE : WRAP_HANDLE,
+            inWidth, inHeight, inFormat, inLayerCount, static_cast<uint64_t>(inUsage),
+            inStride)
 {
-    width  = buffer->width;
-    height = buffer->height;
-    stride = buffer->stride;
-    format = buffer->format;
-    usage  = buffer->usage;
-    handle = buffer->handle;
+}
+
+GraphicBuffer::GraphicBuffer(const native_handle_t* handle,
+        HandleWrapMethod method, uint32_t width, uint32_t height,
+        PixelFormat format, uint32_t layerCount,
+        uint64_t usage,
+        uint32_t stride)
+    : GraphicBuffer()
+{
+    mInitCheck = initWithHandle(handle, method, width, height, format,
+            layerCount, usage, stride);
 }
 
 GraphicBuffer::~GraphicBuffer()
@@ -107,15 +104,12 @@
 void GraphicBuffer::free_handle()
 {
     if (mOwner == ownHandle) {
-        mBufferMapper.unregisterBuffer(handle);
-        native_handle_close(handle);
-        native_handle_delete(const_cast<native_handle*>(handle));
+        mBufferMapper.freeBuffer(handle);
     } else if (mOwner == ownData) {
         GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
         allocator.free(handle);
     }
     handle = NULL;
-    mWrappedBuffer = 0;
 }
 
 status_t GraphicBuffer::initCheck() const {
@@ -135,7 +129,7 @@
 }
 
 status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage)
+        PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
 {
     if (mOwner != ownData)
         return INVALID_OPERATION;
@@ -144,6 +138,7 @@
             static_cast<int>(inWidth) == width &&
             static_cast<int>(inHeight) == height &&
             inFormat == format &&
+            inLayerCount == layerCount &&
             static_cast<int>(inUsage) == usage)
         return NO_ERROR;
 
@@ -152,36 +147,78 @@
         allocator.free(handle);
         handle = 0;
     }
-    return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+    return initWithSize(inWidth, inHeight, inFormat, inLayerCount,
+            inUsage, "[Reallocation]");
 }
 
 bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage)
+        PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
 {
     if (static_cast<int>(inWidth) != width) return true;
     if (static_cast<int>(inHeight) != height) return true;
     if (inFormat != format) return true;
+    if (inLayerCount != layerCount) return true;
     if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
     return false;
 }
 
-status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
+status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
+        PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
+        std::string requestorName)
 {
     GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
     uint32_t outStride = 0;
-    status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage,
-            &handle, &outStride, mId, std::move(requestorName));
+    status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
+            inUsage, &handle, &outStride, mId,
+            std::move(requestorName));
     if (err == NO_ERROR) {
         width = static_cast<int>(inWidth);
         height = static_cast<int>(inHeight);
         format = inFormat;
+        layerCount = inLayerCount;
         usage = static_cast<int>(inUsage);
         stride = static_cast<int>(outStride);
     }
     return err;
 }
 
+status_t GraphicBuffer::initWithHandle(const native_handle_t* handle,
+        HandleWrapMethod method, uint32_t width, uint32_t height,
+        PixelFormat format, uint32_t layerCount, uint64_t usage,
+        uint32_t stride)
+{
+    ANativeWindowBuffer::width  = static_cast<int>(width);
+    ANativeWindowBuffer::height = static_cast<int>(height);
+    ANativeWindowBuffer::stride = static_cast<int>(stride);
+    ANativeWindowBuffer::format = format;
+    ANativeWindowBuffer::usage  = static_cast<int>(usage);
+
+    ANativeWindowBuffer::layerCount = layerCount;
+
+    mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle;
+
+    if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) {
+        buffer_handle_t importedHandle;
+        status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
+        if (err != NO_ERROR) {
+            initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0);
+
+            return err;
+        }
+
+        if (method == TAKE_UNREGISTERED_HANDLE) {
+            native_handle_close(handle);
+            native_handle_delete(const_cast<native_handle_t*>(handle));
+        }
+
+        handle = importedHandle;
+    }
+
+    ANativeWindowBuffer::handle = handle;
+
+    return NO_ERROR;
+}
+
 status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr)
 {
     const Rect lockBounds(width, height);
@@ -239,6 +276,12 @@
 status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
         void** vaddr, int fenceFd)
 {
+    return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd);
+}
+
+status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage,
+        uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd)
+{
     if (rect.left < 0 || rect.right  > width ||
         rect.top  < 0 || rect.bottom > height) {
         ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -246,8 +289,8 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lockAsync(handle, inUsage, rect, vaddr,
-            fenceFd);
+    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
+            inConsumerUsage, rect, vaddr, fenceFd);
     return res;
 }
 
@@ -281,7 +324,7 @@
 }
 
 size_t GraphicBuffer::getFlattenedSize() const {
-    return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
+    return static_cast<size_t>(12 + (handle ? handle->numInts : 0)) * sizeof(int);
 }
 
 size_t GraphicBuffer::getFdCount() const {
@@ -301,19 +344,20 @@
     buf[2] = height;
     buf[3] = stride;
     buf[4] = format;
-    buf[5] = usage;
-    buf[6] = static_cast<int32_t>(mId >> 32);
-    buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
-    buf[8] = static_cast<int32_t>(mGenerationNumber);
-    buf[9] = 0;
+    buf[5] = static_cast<int32_t>(layerCount);
+    buf[6] = usage;
+    buf[7] = static_cast<int32_t>(mId >> 32);
+    buf[8] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
+    buf[9] = static_cast<int32_t>(mGenerationNumber);
     buf[10] = 0;
+    buf[11] = 0;
 
     if (handle) {
-        buf[9] = handle->numFds;
-        buf[10] = handle->numInts;
+        buf[10] = handle->numFds;
+        buf[11] = handle->numInts;
         memcpy(fds, handle->data,
                 static_cast<size_t>(handle->numFds) * sizeof(int));
-        memcpy(&buf[11], handle->data + handle->numFds,
+        memcpy(&buf[12], handle->data + handle->numFds,
                 static_cast<size_t>(handle->numInts) * sizeof(int));
     }
 
@@ -329,28 +373,28 @@
 
 status_t GraphicBuffer::unflatten(
         void const*& buffer, size_t& size, int const*& fds, size_t& count) {
-    if (size < 11 * sizeof(int)) return NO_MEMORY;
+    if (size < 12 * sizeof(int)) return NO_MEMORY;
 
     int const* buf = static_cast<int const*>(buffer);
     if (buf[0] != 'GBFR') return BAD_TYPE;
 
-    const size_t numFds  = static_cast<size_t>(buf[9]);
-    const size_t numInts = static_cast<size_t>(buf[10]);
+    const size_t numFds  = static_cast<size_t>(buf[10]);
+    const size_t numInts = static_cast<size_t>(buf[11]);
 
     // Limit the maxNumber to be relatively small. The number of fds or ints
     // should not come close to this number, and the number itself was simply
     // chosen to be high enough to not cause issues and low enough to prevent
     // overflow problems.
     const size_t maxNumber = 4096;
-    if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
-        width = height = stride = format = usage = 0;
+    if (numFds >= maxNumber || numInts >= (maxNumber - 12)) {
+        width = height = stride = format = layerCount = usage = 0;
         handle = NULL;
         ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
                 numFds, numInts);
         return BAD_VALUE;
     }
 
-    const size_t sizeNeeded = (11 + numInts) * sizeof(int);
+    const size_t sizeNeeded = (12 + numInts) * sizeof(int);
     if (size < sizeNeeded) return NO_MEMORY;
 
     size_t fdCountNeeded = numFds;
@@ -366,39 +410,45 @@
         height = buf[2];
         stride = buf[3];
         format = buf[4];
-        usage  = buf[5];
+        layerCount = static_cast<uintptr_t>(buf[5]);
+        usage  = buf[6];
         native_handle* h = native_handle_create(
                 static_cast<int>(numFds), static_cast<int>(numInts));
         if (!h) {
-            width = height = stride = format = usage = 0;
+            width = height = stride = format = layerCount = usage = 0;
             handle = NULL;
             ALOGE("unflatten: native_handle_create failed");
             return NO_MEMORY;
         }
         memcpy(h->data, fds, numFds * sizeof(int));
-        memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
+        memcpy(h->data + numFds, &buf[12], numInts * sizeof(int));
         handle = h;
     } else {
-        width = height = stride = format = usage = 0;
+        width = height = stride = format = layerCount = usage = 0;
         handle = NULL;
     }
 
-    mId = static_cast<uint64_t>(buf[6]) << 32;
-    mId |= static_cast<uint32_t>(buf[7]);
+    mId = static_cast<uint64_t>(buf[7]) << 32;
+    mId |= static_cast<uint32_t>(buf[8]);
 
-    mGenerationNumber = static_cast<uint32_t>(buf[8]);
+    mGenerationNumber = static_cast<uint32_t>(buf[9]);
 
     mOwner = ownHandle;
 
     if (handle != 0) {
-        status_t err = mBufferMapper.registerBuffer(this);
+        buffer_handle_t importedHandle;
+        status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
         if (err != NO_ERROR) {
-            width = height = stride = format = usage = 0;
+            width = height = stride = format = layerCount = usage = 0;
             handle = NULL;
             ALOGE("unflatten: registerBuffer failed: %s (%d)",
                     strerror(-err), err);
             return err;
         }
+
+        native_handle_close(handle);
+        native_handle_delete(const_cast<native_handle_t*>(handle));
+        handle = importedHandle;
     }
 
     buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index edfff4d..eaba1ed 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,14 +18,19 @@
 #define LOG_TAG "GraphicBufferAllocator"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <cutils/log.h>
+#include <ui/GraphicBufferAllocator.h>
 
+#include <stdio.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
+
+#include <log/log.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/Gralloc1On0Adapter.h>
+#include <ui/Gralloc2.h>
+#include <ui/GraphicBufferMapper.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -37,8 +42,11 @@
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
 GraphicBufferAllocator::GraphicBufferAllocator()
-  : mLoader(std::make_unique<Gralloc1::Loader>()),
-    mDevice(mLoader->getDevice()) {}
+  : mMapper(GraphicBufferMapper::getInstance()),
+    mAllocator(std::make_unique<Gralloc2::Allocator>(
+                mMapper.getGrallocMapper()))
+{
+}
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
 
@@ -55,22 +63,25 @@
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
         if (rec.size) {
-            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
-                    list.keyAt(i), rec.size/1024.0f,
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
-                    rec.requestorName.c_str());
+            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+                    " | %s\n",
+                    list.keyAt(i), rec.size/1024.0,
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.usage, rec.requestorName.c_str());
         } else {
-            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+                    " | %s\n",
                     list.keyAt(i),
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
-                    rec.requestorName.c_str());
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.usage, rec.requestorName.c_str());
         }
         result.append(buffer);
         total += rec.size;
     }
-    snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f);
+    snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0);
     result.append(buffer);
-    std::string deviceDump = mDevice->dump();
+
+    std::string deviceDump = mAllocator->dumpDebugInfo();
     result.append(deviceDump.c_str(), deviceDump.size());
 }
 
@@ -82,8 +93,9 @@
 }
 
 status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t usage, buffer_handle_t* handle,
-        uint32_t* stride, uint64_t graphicBufferId, std::string requestorName)
+        PixelFormat format, uint32_t layerCount, uint64_t usage,
+        buffer_handle_t* handle, uint32_t* stride,
+        uint64_t /*graphicBufferId*/, std::string requestorName)
 {
     ATRACE_CALL();
 
@@ -92,46 +104,19 @@
     if (!width || !height)
         width = height = 1;
 
-    // Filter out any usage bits that should not be passed to the gralloc module
-    usage &= GRALLOC_USAGE_ALLOC_MASK;
+    // Ensure that layerCount is valid.
+    if (layerCount < 1)
+        layerCount = 1;
 
-    auto descriptor = mDevice->createDescriptor();
-    auto error = descriptor->setDimensions(width, height);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setFormat(static_cast<android_pixel_format_t>(format));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set format to %d: %d", format, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setProducerUsage(
-            static_cast<gralloc1_producer_usage_t>(usage));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set producer usage to %u: %d", usage, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setConsumerUsage(
-            static_cast<gralloc1_consumer_usage_t>(usage));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set consumer usage to %u: %d", usage, error);
-        return BAD_VALUE;
-    }
+    Gralloc2::IMapper::BufferDescriptorInfo info = {};
+    info.width = width;
+    info.height = height;
+    info.layerCount = layerCount;
+    info.format = static_cast<Gralloc2::PixelFormat>(format);
+    info.usage = usage;
 
-    error = mDevice->allocate(descriptor, graphicBufferId, handle);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
-                width, height, format, usage, error);
-        return NO_MEMORY;
-    }
-
-    error = mDevice->getStride(*handle, stride);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGW("Failed to get stride from buffer: %d", error);
-    }
-
-    if (error == NO_ERROR) {
+    Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
+    if (error == Gralloc2::Error::NONE) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
         uint32_t bpp = bytesPerPixel(format);
@@ -140,23 +125,29 @@
         rec.height = height;
         rec.stride = *stride;
         rec.format = format;
+        rec.layerCount = layerCount;
         rec.usage = usage;
         rec.size = static_cast<size_t>(height * (*stride) * bpp);
         rec.requestorName = std::move(requestorName);
         list.add(*handle, rec);
-    }
 
-    return NO_ERROR;
+        return NO_ERROR;
+    } else {
+        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+                "usage %" PRIx64 ": %d",
+                width, height, layerCount, format, usage,
+                error);
+        return NO_MEMORY;
+    }
 }
 
 status_t GraphicBufferAllocator::free(buffer_handle_t handle)
 {
     ATRACE_CALL();
 
-    auto error = mDevice->release(handle);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to free buffer: %d", error);
-    }
+    // We allocated a buffer from the allocator and imported it into the
+    // mapper to get the handle.  We just need to free the handle now.
+    mMapper.freeBuffer(handle);
 
     Mutex::Autolock _l(sLock);
     KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 481d43c..b9fa640 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -18,8 +18,9 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
-#include <stdint.h>
-#include <errno.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
 
 // We would eliminate the non-conforming zero-length array, but we can't since
 // this is effectively included from the Linux kernel
@@ -28,13 +29,11 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-#include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
-#include <ui/Gralloc1On0Adapter.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/Rect.h>
+#include <ui/Gralloc2.h>
+#include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
 
@@ -44,46 +43,35 @@
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
 
 GraphicBufferMapper::GraphicBufferMapper()
-  : mLoader(std::make_unique<Gralloc1::Loader>()),
-    mDevice(mLoader->getDevice()) {}
+  : mMapper(std::make_unique<const Gralloc2::Mapper>())
+{
+}
 
-
-
-status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle)
+status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
+        buffer_handle_t* outHandle)
 {
     ATRACE_CALL();
 
-    gralloc1_error_t error = mDevice->retain(handle);
-    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
-            handle, error);
+    Gralloc2::Error error = mMapper->importBuffer(
+            hardware::hidl_handle(rawHandle), outHandle);
 
-    return error;
+    ALOGW_IF(error != Gralloc2::Error::NONE, "importBuffer(%p) failed: %d",
+            rawHandle, error);
+
+    return static_cast<status_t>(error);
 }
 
-status_t GraphicBufferMapper::registerBuffer(const GraphicBuffer* buffer)
+status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle)
 {
     ATRACE_CALL();
 
-    gralloc1_error_t error = mDevice->retain(buffer);
-    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
-            buffer->getNativeBuffer()->handle, error);
+    mMapper->freeBuffer(handle);
 
-    return error;
+    return NO_ERROR;
 }
 
-status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle)
-{
-    ATRACE_CALL();
-
-    gralloc1_error_t error = mDevice->release(handle);
-    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
-            handle, error);
-
-    return error;
-}
-
-static inline gralloc1_rect_t asGralloc1Rect(const Rect& rect) {
-    gralloc1_rect_t outRect{};
+static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
+    Gralloc2::IMapper::Rect outRect{};
     outRect.left = rect.left;
     outRect.top = rect.top;
     outRect.width = rect.width();
@@ -117,18 +105,24 @@
 status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
         uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
 {
+    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+}
+
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
+        uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+        void** vaddr, int fenceFd)
+{
     ATRACE_CALL();
 
-    gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
-    sp<Fence> fence = new Fence(fenceFd);
-    gralloc1_error_t error = mDevice->lock(handle,
-            static_cast<gralloc1_producer_usage_t>(usage),
-            static_cast<gralloc1_consumer_usage_t>(usage),
-            &accessRegion, vaddr, fence);
-    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
-            error);
+    const uint64_t usage = static_cast<uint64_t>(
+            android_convertGralloc1To0Usage(producerUsage, consumerUsage));
+    Gralloc2::Error error = mMapper->lock(handle, usage,
+            asGralloc2Rect(bounds), fenceFd, vaddr);
 
-    return error;
+    ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
+            handle, error);
+
+    return static_cast<status_t>(error);
 }
 
 static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) {
@@ -159,132 +153,28 @@
 {
     ATRACE_CALL();
 
-    gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
-    sp<Fence> fence = new Fence(fenceFd);
-
-    if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
-        gralloc1_error_t error = mDevice->lockYCbCr(handle,
-                static_cast<gralloc1_producer_usage_t>(usage),
-                static_cast<gralloc1_consumer_usage_t>(usage),
-                &accessRegion, ycbcr, fence);
-        ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d",
-                handle, error);
-        return error;
+    Gralloc2::YCbCrLayout layout;
+    Gralloc2::Error error = mMapper->lock(handle, usage,
+            asGralloc2Rect(bounds), fenceFd, &layout);
+    if (error == Gralloc2::Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
     }
 
-    uint32_t numPlanes = 0;
-    gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGV("Failed to retrieve number of flex planes: %d", error);
-        return error;
-    }
-    if (numPlanes < 3) {
-        ALOGV("Not enough planes for YCbCr (%u found)", numPlanes);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    std::vector<android_flex_plane_t> planes(numPlanes);
-    android_flex_layout_t flexLayout{};
-    flexLayout.num_planes = numPlanes;
-    flexLayout.planes = planes.data();
-
-    error = mDevice->lockFlex(handle,
-            static_cast<gralloc1_producer_usage_t>(usage),
-            static_cast<gralloc1_consumer_usage_t>(usage),
-            &accessRegion, &flexLayout, fence);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGW("lockFlex(%p, ...) failed: %d", handle, error);
-        return error;
-    }
-    if (flexLayout.format != FLEX_FORMAT_YCbCr) {
-        ALOGV("Unable to convert flex-format buffer to YCbCr");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    // Find planes
-    auto yPlane = planes.cend();
-    auto cbPlane = planes.cend();
-    auto crPlane = planes.cend();
-    for (auto planeIter = planes.cbegin(); planeIter != planes.cend();
-            ++planeIter) {
-        if (planeIter->component == FLEX_COMPONENT_Y) {
-            yPlane = planeIter;
-        } else if (planeIter->component == FLEX_COMPONENT_Cb) {
-            cbPlane = planeIter;
-        } else if (planeIter->component == FLEX_COMPONENT_Cr) {
-            crPlane = planeIter;
-        }
-    }
-    if (yPlane == planes.cend()) {
-        ALOGV("Unable to find Y plane");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (cbPlane == planes.cend()) {
-        ALOGV("Unable to find Cb plane");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (crPlane == planes.cend()) {
-        ALOGV("Unable to find Cr plane");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    // Validate planes
-    if (!isValidYCbCrPlane(*yPlane)) {
-        ALOGV("Y plane is invalid");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (!isValidYCbCrPlane(*cbPlane)) {
-        ALOGV("Cb plane is invalid");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (!isValidYCbCrPlane(*crPlane)) {
-        ALOGV("Cr plane is invalid");
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (cbPlane->v_increment != crPlane->v_increment) {
-        ALOGV("Cb and Cr planes have different step (%d vs. %d)",
-                cbPlane->v_increment, crPlane->v_increment);
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-    if (cbPlane->h_increment != crPlane->h_increment) {
-        ALOGV("Cb and Cr planes have different stride (%d vs. %d)",
-                cbPlane->h_increment, crPlane->h_increment);
-        unlock(handle);
-        return GRALLOC1_ERROR_UNSUPPORTED;
-    }
-
-    // Pack plane data into android_ycbcr struct
-    ycbcr->y = yPlane->top_left;
-    ycbcr->cb = cbPlane->top_left;
-    ycbcr->cr = crPlane->top_left;
-    ycbcr->ystride = static_cast<size_t>(yPlane->v_increment);
-    ycbcr->cstride = static_cast<size_t>(cbPlane->v_increment);
-    ycbcr->chroma_step = static_cast<size_t>(cbPlane->h_increment);
-
-    return error;
+    return static_cast<status_t>(error);
 }
 
 status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
 {
     ATRACE_CALL();
 
-    sp<Fence> fence = Fence::NO_FENCE;
-    gralloc1_error_t error = mDevice->unlock(handle, &fence);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("unlock(%p) failed: %d", handle, error);
-        return error;
-    }
+    *fenceFd = mMapper->unlock(handle);
 
-    *fenceFd = fence->dup();
-    return error;
+    return NO_ERROR;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/GraphicsEnv.cpp b/libs/ui/GraphicsEnv.cpp
new file mode 100644
index 0000000..8182c07
--- /dev/null
+++ b/libs/ui/GraphicsEnv.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 1
+#define LOG_TAG "GraphicsEnv"
+#include <ui/GraphicsEnv.h>
+
+#include <mutex>
+
+#include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
+
+// TODO(b/37049319) Get this from a header once one exists
+extern "C" {
+  android_namespace_t* android_get_exported_namespace(const char*);
+}
+
+namespace android {
+
+/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
+    static GraphicsEnv env;
+    return env;
+}
+
+void GraphicsEnv::setDriverPath(const std::string path) {
+    if (!mDriverPath.empty()) {
+        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
+                mDriverPath.c_str(), path.c_str());
+        return;
+    }
+    ALOGV("setting driver path to '%s'", path.c_str());
+    mDriverPath = path;
+}
+
+android_namespace_t* GraphicsEnv::getDriverNamespace() {
+    static std::once_flag once;
+    std::call_once(once, [this]() {
+        if (mDriverPath.empty())
+            return;
+        // If the sphal namespace isn't configured for a device, don't support updatable drivers.
+        // We need a parent namespace to inherit the default search path from.
+        auto sphalNamespace = android_get_exported_namespace("sphal");
+        if (!sphalNamespace) return;
+        mDriverNamespace = android_create_namespace("gfx driver",
+                                                    nullptr,             // ld_library_path
+                                                    mDriverPath.c_str(), // default_library_path
+                                                    ANDROID_NAMESPACE_TYPE_SHARED |
+                                                            ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                                    nullptr, // permitted_when_isolated_path
+                                                    sphalNamespace);
+    });
+    return mDriverNamespace;
+}
+
+} // namespace android
+
+extern "C" android_namespace_t* android_getDriverNamespace() {
+    return android::GraphicsEnv::getInstance().getDriverNamespace();
+}
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
index 511f68a..39adc5e 100644
--- a/libs/ui/HdrCapabilities.cpp
+++ b/libs/ui/HdrCapabilities.cpp
@@ -16,44 +16,76 @@
 
 #include <ui/HdrCapabilities.h>
 
-#include <binder/Parcel.h>
-
 namespace android {
 
-status_t HdrCapabilities::writeToParcel(Parcel* parcel) const
-{
-    status_t result = parcel->writeInt32Vector(mSupportedHdrTypes);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMaxLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMaxAverageLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMinLuminance);
-    return result;
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
+#endif
+
+HdrCapabilities::~HdrCapabilities() = default;
+HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) = default;
+HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default;
+
+
+size_t HdrCapabilities::getFlattenedSize() const {
+    return  sizeof(mMaxLuminance) +
+            sizeof(mMaxAverageLuminance) +
+            sizeof(mMinLuminance) +
+            sizeof(int32_t) +
+            mSupportedHdrTypes.size() * sizeof(int32_t);
 }
 
-status_t HdrCapabilities::readFromParcel(const Parcel* parcel)
-{
-    status_t result = parcel->readInt32Vector(&mSupportedHdrTypes);
-    if (result != OK) {
-        return result;
+status_t HdrCapabilities::flatten(void* buffer, size_t size) const {
+
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
     }
-    result = parcel->readFloat(&mMaxLuminance);
-    if (result != OK) {
-        return result;
+
+    int32_t* const buf = static_cast<int32_t*>(buffer);
+    reinterpret_cast<float&>(buf[0]) = mMaxLuminance;
+    reinterpret_cast<float&>(buf[1]) = mMaxAverageLuminance;
+    reinterpret_cast<float&>(buf[2]) = mMinLuminance;
+    buf[3] = static_cast<int32_t>(mSupportedHdrTypes.size());
+    for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) {
+        buf[4 + i] = mSupportedHdrTypes[i];
     }
-    result = parcel->readFloat(&mMaxAverageLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->readFloat(&mMinLuminance);
-    return result;
+    return NO_ERROR;
 }
 
+status_t HdrCapabilities::unflatten(void const* buffer, size_t size) {
+
+    size_t minSize = sizeof(mMaxLuminance) +
+                     sizeof(mMaxAverageLuminance) +
+                     sizeof(mMinLuminance) +
+                     sizeof(int32_t);
+
+    if (size < minSize) {
+        return NO_MEMORY;
+    }
+
+    int32_t const * const buf = static_cast<int32_t const *>(buffer);
+    const size_t itemCount = size_t(buf[3]);
+
+    // check the buffer is large enough
+    if (size < minSize + itemCount * sizeof(int32_t)) {
+        return BAD_VALUE;
+    }
+
+    mMaxLuminance        = reinterpret_cast<float const&>(buf[0]);
+    mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]);
+    mMinLuminance        = reinterpret_cast<float const&>(buf[2]);
+    if (itemCount) {
+        mSupportedHdrTypes.reserve(itemCount);
+        for (size_t i = 0; i < itemCount; ++i) {
+            mSupportedHdrTypes[i] = buf[4 + i];
+        }
+    }
+    return NO_ERROR;
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 } // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index cab1dde..e88fdd5 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -22,9 +22,12 @@
 
 uint32_t bytesPerPixel(PixelFormat format) {
     switch (format) {
+        case PIXEL_FORMAT_RGBA_FP16:
+            return 8;
         case PIXEL_FORMAT_RGBA_8888:
         case PIXEL_FORMAT_RGBX_8888:
         case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_1010102:
             return 4;
         case PIXEL_FORMAT_RGB_888:
             return 3;
@@ -38,9 +41,12 @@
 
 uint32_t bitsPerPixel(PixelFormat format) {
     switch (format) {
+        case PIXEL_FORMAT_RGBA_FP16:
+            return 64;
         case PIXEL_FORMAT_RGBA_8888:
         case PIXEL_FORMAT_RGBX_8888:
         case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_1010102:
             return 32;
         case PIXEL_FORMAT_RGB_888:
             return 24;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index ee152bf..b53c563 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -294,7 +294,7 @@
 Region& Region::subtractSelf(const Rect& r) {
     return operationSelf(r, op_nand);
 }
-Region& Region::operationSelf(const Rect& r, int op) {
+Region& Region::operationSelf(const Rect& r, uint32_t op) {
     Region lhs(*this);
     boolean_operation(op, *this, lhs, r);
     return *this;
@@ -314,7 +314,7 @@
 Region& Region::subtractSelf(const Region& rhs) {
     return operationSelf(rhs, op_nand);
 }
-Region& Region::operationSelf(const Region& rhs, int op) {
+Region& Region::operationSelf(const Region& rhs, uint32_t op) {
     Region lhs(*this);
     boolean_operation(op, *this, lhs, rhs);
     return *this;
@@ -339,7 +339,7 @@
 const Region Region::subtract(const Rect& rhs) const {
     return operation(rhs, op_nand);
 }
-const Region Region::operation(const Rect& rhs, int op) const {
+const Region Region::operation(const Rect& rhs, uint32_t op) const {
     Region result;
     boolean_operation(op, result, *this, rhs);
     return result;
@@ -359,7 +359,7 @@
 const Region Region::subtract(const Region& rhs) const {
     return operation(rhs, op_nand);
 }
-const Region Region::operation(const Region& rhs, int op) const {
+const Region Region::operation(const Region& rhs, uint32_t op) const {
     Region result;
     boolean_operation(op, result, *this, rhs);
     return result;
@@ -385,7 +385,7 @@
 Region& Region::subtractSelf(const Region& rhs, int dx, int dy) {
     return operationSelf(rhs, dx, dy, op_nand);
 }
-Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) {
+Region& Region::operationSelf(const Region& rhs, int dx, int dy, uint32_t op) {
     Region lhs(*this);
     boolean_operation(op, *this, lhs, rhs, dx, dy);
     return *this;
@@ -405,7 +405,7 @@
 const Region Region::subtract(const Region& rhs, int dx, int dy) const {
     return operation(rhs, dx, dy, op_nand);
 }
-const Region Region::operation(const Region& rhs, int dx, int dy, int op) const {
+const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) const {
     Region result;
     boolean_operation(op, result, *this, rhs, dx, dy);
     return result;
@@ -424,7 +424,7 @@
     Vector<Rect> span;
     Rect* cur;
 public:
-    rasterizer(Region& reg)
+    explicit rasterizer(Region& reg)
         : bounds(INT_MAX, 0, INT_MIN, 0), storage(reg.mStorage), head(), tail(), cur() {
         storage.clear();
     }
@@ -489,7 +489,8 @@
                     merge = false;
                     break;
                 }
-                p++, q++;
+                p++;
+                q++;
             }
         }
     }
@@ -582,7 +583,7 @@
     return result;
 }
 
-void Region::boolean_operation(int op, Region& dst,
+void Region::boolean_operation(uint32_t op, Region& dst,
         const Region& lhs,
         const Region& rhs, int dx, int dy)
 {
@@ -692,7 +693,7 @@
 #endif
 }
 
-void Region::boolean_operation(int op, Region& dst,
+void Region::boolean_operation(uint32_t op, Region& dst,
         const Region& lhs,
         const Rect& rhs, int dx, int dy)
 {
@@ -721,13 +722,13 @@
 #endif
 }
 
-void Region::boolean_operation(int op, Region& dst,
+void Region::boolean_operation(uint32_t op, Region& dst,
         const Region& lhs, const Region& rhs)
 {
     boolean_operation(op, dst, lhs, rhs, 0, 0);
 }
 
-void Region::boolean_operation(int op, Region& dst,
+void Region::boolean_operation(uint32_t op, Region& dst,
         const Region& lhs, const Rect& rhs)
 {
     boolean_operation(op, dst, lhs, rhs, 0, 0);
diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp
index 9e7ba8e..7730690 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/ui/UiConfig.cpp
@@ -18,20 +18,10 @@
 
 namespace android {
 
-#ifdef FRAMEBUFFER_FORCE_FORMAT
-// We need the two-level macro to stringify the contents of a macro argument
-#define STRINGIFY(x) #x
-#define TOSTRING(x) STRINGIFY(x)
-#endif
-
 void appendUiConfigString(String8& configStr)
 {
     static const char* config =
-            " [libui"
-#ifdef FRAMEBUFFER_FORCE_FORMAT
-            " FRAMEBUFFER_FORCE_FORMAT=" TOSTRING(FRAMEBUFFER_FORCE_FORMAT)
-#endif
-            "]";
+            " [libui]";
     configStr.append(config);
 }
 
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
new file mode 100644
index 0000000..6733505
--- /dev/null
+++ b/libs/ui/tests/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "Region_test",
+    shared_libs: ["libui"],
+    srcs: ["Region_test.cpp"],
+}
+
+cc_test {
+    name: "colorspace_test",
+    shared_libs: ["libui"],
+    srcs: ["colorspace_test.cpp"],
+}
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
deleted file mode 100644
index 6438b1f..0000000
--- a/libs/ui/tests/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SHARED_LIBRARIES := libui
-LOCAL_SRC_FILES := Region_test.cpp
-LOCAL_MODULE := Region_test
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SRC_FILES := vec_test.cpp
-LOCAL_MODULE := vec_test
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SRC_FILES := mat_test.cpp
-LOCAL_MODULE := mat_test
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
new file mode 100644
index 0000000..0a4873c
--- /dev/null
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ColorSpaceTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <ui/ColorSpace.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class ColorSpaceTest : public testing::Test {
+protected:
+};
+
+TEST_F(ColorSpaceTest, XYZ) {
+    mat3 sRGBToXYZ(transpose(mat3{
+        0.412391f, 0.357584f, 0.180481f,
+        0.212639f, 0.715169f, 0.072192f,
+        0.019331f, 0.119195f, 0.950532f
+    }));
+
+    mat3 XYZtoSRGB(inverse(sRGBToXYZ));
+
+    ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+    EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ());
+    EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB());
+}
+
+TEST_F(ColorSpaceTest, XYZPrimaries) {
+    mat3 sRGBToXYZ(transpose(mat3{
+        0.412391f, 0.357584f, 0.180481f,
+        0.212639f, 0.715169f, 0.072192f,
+        0.019331f, 0.119195f, 0.950532f
+    }));
+
+    ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+    EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f);
+    EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f);
+
+    EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f);
+    EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f);
+
+    EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f);
+    EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZWhitePoint) {
+    mat3 sRGBToXYZ(transpose(mat3{
+        0.412391f, 0.357584f, 0.180481f,
+        0.212639f, 0.715169f, 0.072192f,
+        0.019331f, 0.119195f, 0.950532f
+    }));
+
+    ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+    EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f);
+    EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZFromPrimaries) {
+    mat3 sRGBToXYZ(transpose(mat3{
+        0.412391f, 0.357584f, 0.180481f,
+        0.212639f, 0.715169f, 0.072192f,
+        0.019331f, 0.119195f, 0.950532f
+    }));
+
+    ColorSpace sRGB1("sRGB", sRGBToXYZ);
+    ColorSpace sRGB2(
+          "sRGB",
+          {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+          {0.3127f, 0.3290f}
+    );
+
+    for (size_t i = 0; i < 3; i++) {
+        for (size_t j= 0; j < 3; j++) {
+            ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f);
+        }
+    }
+
+    for (size_t i = 0; i < 3; i++) {
+        for (size_t j= 0; j < 3; j++) {
+            ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f);
+        }
+    }
+}
+
+TEST_F(ColorSpaceTest, TransferFunctions) {
+    ColorSpace sRGB = ColorSpace::sRGB();
+
+    EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f);
+    EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f);
+    EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
+    EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
+
+    for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
+        ASSERT_TRUE(v >= sRGB.getEOTF()(v));
+        ASSERT_TRUE(v <= sRGB.getOETF()(v));
+    }
+
+    float previousEOTF = std::numeric_limits<float>::lowest();
+    float previousOETF = std::numeric_limits<float>::lowest();
+    for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+        ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
+        previousEOTF = sRGB.getEOTF()(v);
+        ASSERT_TRUE(previousOETF < sRGB.getOETF()(v));
+        previousOETF = sRGB.getOETF()(v);
+    }
+
+    ColorSpace sRGB2(
+          "sRGB",
+          {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+          {0.3127f, 0.3290f}
+          // linear transfer functions
+    );
+    for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+        ASSERT_EQ(v, sRGB2.getEOTF()(v));
+        ASSERT_EQ(v, sRGB2.getOETF()(v));
+    }
+}
+
+TEST_F(ColorSpaceTest, Clamping) {
+    // Pick a color outside of sRGB
+    float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0}));
+
+    // The color will be clamped
+    float3 sRGB(ColorSpace::sRGB().xyzToRGB(c));
+    EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0});
+
+    // The color will not be clamped
+    float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c));
+    EXPECT_TRUE(extendedSRGB.g > 1.0f);
+}
+
+TEST_F(ColorSpaceTest, Connect) {
+    // No chromatic adaptation
+    auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
+            .transform({1.0f, 0.5f, 0.0f});
+    EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
+
+    // Test with chromatic adaptation
+    r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
+            .transform({1.0f, 0.0f, 0.0f});
+    EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
+}
+
+TEST_F(ColorSpaceTest, LUT) {
+    auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB());
+    EXPECT_TRUE(lut != nullptr);
+
+    // {1.0f, 0.5f, 0.0f}
+    auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16];
+    EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
+
+    // {1.0f, 1.0f, 0.5f}
+    r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
+    EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f})));
+
+    // {1.0f, 1.0f, 1.0f}
+    r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
+    EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f})));
+
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp
deleted file mode 100644
index a2c63ac..0000000
--- a/libs/ui/tests/mat_test.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RegionTest"
-
-#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <gtest/gtest.h>
-
-#include <ui/mat4.h>
-
-namespace android {
-
-class MatTest : public testing::Test {
-protected:
-};
-
-TEST_F(MatTest, Basics) {
-    mat4 m0;
-    EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
-}
-
-TEST_F(MatTest, ComparisonOps) {
-    mat4 m0;
-    mat4 m1(2);
-
-    EXPECT_TRUE(m0 == m0);
-    EXPECT_TRUE(m0 != m1);
-    EXPECT_FALSE(m0 != m0);
-    EXPECT_FALSE(m0 == m1);
-}
-
-TEST_F(MatTest, Constructors) {
-    mat4 m0;
-    ASSERT_EQ(m0[0].x, 1);
-    ASSERT_EQ(m0[0].y, 0);
-    ASSERT_EQ(m0[0].z, 0);
-    ASSERT_EQ(m0[0].w, 0);
-    ASSERT_EQ(m0[1].x, 0);
-    ASSERT_EQ(m0[1].y, 1);
-    ASSERT_EQ(m0[1].z, 0);
-    ASSERT_EQ(m0[1].w, 0);
-    ASSERT_EQ(m0[2].x, 0);
-    ASSERT_EQ(m0[2].y, 0);
-    ASSERT_EQ(m0[2].z, 1);
-    ASSERT_EQ(m0[2].w, 0);
-    ASSERT_EQ(m0[3].x, 0);
-    ASSERT_EQ(m0[3].y, 0);
-    ASSERT_EQ(m0[3].z, 0);
-    ASSERT_EQ(m0[3].w, 1);
-
-    mat4 m1(2);
-    mat4 m2(vec4(2));
-    mat4 m3(m2);
-
-    EXPECT_EQ(m1, m2);
-    EXPECT_EQ(m2, m3);
-    EXPECT_EQ(m3, m1);
-
-    mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
-}
-
-TEST_F(MatTest, ArithmeticOps) {
-    mat4 m0;
-    mat4 m1(2);
-    mat4 m2(vec4(2));
-
-    m1 += m2;
-    EXPECT_EQ(mat4(4), m1);
-
-    m2 -= m1;
-    EXPECT_EQ(mat4(-2), m2);
-
-    m1 *= 2;
-    EXPECT_EQ(mat4(8), m1);
-
-    m1 /= 2;
-    EXPECT_EQ(mat4(4), m1);
-
-    m0 = -m0;
-    EXPECT_EQ(mat4(-1), m0);
-}
-
-TEST_F(MatTest, UnaryOps) {
-    const mat4 identity;
-    mat4 m0;
-
-    ++m0;
-    EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0);
-    EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0);
-
-    --m0;
-    EXPECT_EQ(identity, m0);
-}
-
-TEST_F(MatTest, MiscOps) {
-    const mat4 identity;
-    mat4 m0;
-    EXPECT_EQ(4, trace(m0));
-
-    mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16));
-    mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16));
-    EXPECT_EQ(m1, transpose(m2));
-    EXPECT_EQ(m2, transpose(m1));
-    EXPECT_EQ(vec4(1,6,11,16), diag(m1));
-
-    EXPECT_EQ(identity, inverse(identity));
-
-    mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1));
-    mat4 m3i(inverse(m3));
-    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
-    EXPECT_FLOAT_EQ( 3, m3i[0][1]);
-    EXPECT_FLOAT_EQ( 3, m3i[1][0]);
-    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
-
-    mat4 m3ii(inverse(m3i));
-    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
-    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
-    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
-    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
-
-    EXPECT_EQ(m1, m1*identity);
-}
-
-}; // namespace android
diff --git a/libs/ui/tests/vec_test.cpp b/libs/ui/tests/vec_test.cpp
deleted file mode 100644
index 454c999..0000000
--- a/libs/ui/tests/vec_test.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RegionTest"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <ui/vec4.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class VecTest : public testing::Test {
-};
-
-TEST_F(VecTest, Basics) {
-    vec4 v4;
-    vec3& v3(v4.xyz);
-
-    EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
-    EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
-    EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
-    EXPECT_EQ((void*)&v3, (void*)&v4);
-}
-
-TEST_F(VecTest, Constructors) {
-    vec4 v0;
-    EXPECT_EQ(v0.x, 0);
-    EXPECT_EQ(v0.y, 0);
-    EXPECT_EQ(v0.z, 0);
-    EXPECT_EQ(v0.w, 0);
-
-    vec4 v1(1);
-    EXPECT_EQ(v1.x, 1);
-    EXPECT_EQ(v1.y, 1);
-    EXPECT_EQ(v1.z, 1);
-    EXPECT_EQ(v1.w, 1);
-
-    vec4 v2(1,2,3,4);
-    EXPECT_EQ(v2.x, 1);
-    EXPECT_EQ(v2.y, 2);
-    EXPECT_EQ(v2.z, 3);
-    EXPECT_EQ(v2.w, 4);
-
-    vec4 v3(v2);
-    EXPECT_EQ(v3.x, 1);
-    EXPECT_EQ(v3.y, 2);
-    EXPECT_EQ(v3.z, 3);
-    EXPECT_EQ(v3.w, 4);
-
-    vec4 v4(v3.xyz, 42);
-    EXPECT_EQ(v4.x, 1);
-    EXPECT_EQ(v4.y, 2);
-    EXPECT_EQ(v4.z, 3);
-    EXPECT_EQ(v4.w, 42);
-
-    vec4 v5(vec3(v2.xy, 42), 24);
-    EXPECT_EQ(v5.x, 1);
-    EXPECT_EQ(v5.y, 2);
-    EXPECT_EQ(v5.z, 42);
-    EXPECT_EQ(v5.w, 24);
-
-    tvec4<double> vd(2);
-    EXPECT_EQ(vd.x, 2);
-    EXPECT_EQ(vd.y, 2);
-    EXPECT_EQ(vd.z, 2);
-    EXPECT_EQ(vd.w, 2);
-}
-
-TEST_F(VecTest, Access) {
-    vec4 v0(1,2,3,4);
-    v0.x = 10;
-    v0.y = 20;
-    v0.z = 30;
-    v0.w = 40;
-    EXPECT_EQ(v0.x, 10);
-    EXPECT_EQ(v0.y, 20);
-    EXPECT_EQ(v0.z, 30);
-    EXPECT_EQ(v0.w, 40);
-
-    v0[0] = 100;
-    v0[1] = 200;
-    v0[2] = 300;
-    v0[3] = 400;
-    EXPECT_EQ(v0.x, 100);
-    EXPECT_EQ(v0.y, 200);
-    EXPECT_EQ(v0.z, 300);
-    EXPECT_EQ(v0.w, 400);
-
-    v0.xyz = vec3(1,2,3);
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 400);
-}
-
-TEST_F(VecTest, UnaryOps) {
-    vec4 v0(1,2,3,4);
-
-    v0 += 1;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 3);
-    EXPECT_EQ(v0.z, 4);
-    EXPECT_EQ(v0.w, 5);
-
-    v0 -= 1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    v0 *= 2;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 4);
-    EXPECT_EQ(v0.z, 6);
-    EXPECT_EQ(v0.w, 8);
-
-    v0 /= 2;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    vec4 v1(10, 20, 30, 40);
-
-    v0 += v1;
-    EXPECT_EQ(v0.x, 11);
-    EXPECT_EQ(v0.y, 22);
-    EXPECT_EQ(v0.z, 33);
-    EXPECT_EQ(v0.w, 44);
-
-    v0 -= v1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    v0 *= v1;
-    EXPECT_EQ(v0.x, 10);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 90);
-    EXPECT_EQ(v0.w, 160);
-
-    v0 /= v1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    ++v0;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 3);
-    EXPECT_EQ(v0.z, 4);
-    EXPECT_EQ(v0.w, 5);
-
-    ++++v0;
-    EXPECT_EQ(v0.x, 4);
-    EXPECT_EQ(v0.y, 5);
-    EXPECT_EQ(v0.z, 6);
-    EXPECT_EQ(v0.w, 7);
-
-    --v1;
-    EXPECT_EQ(v1.x,  9);
-    EXPECT_EQ(v1.y, 19);
-    EXPECT_EQ(v1.z, 29);
-    EXPECT_EQ(v1.w, 39);
-
-    v1 = -v1;
-    EXPECT_EQ(v1.x,  -9);
-    EXPECT_EQ(v1.y, -19);
-    EXPECT_EQ(v1.z, -29);
-    EXPECT_EQ(v1.w, -39);
-
-    tvec4<double> dv(1,2,3,4);
-    v1 += dv;
-    EXPECT_EQ(v1.x,  -8);
-    EXPECT_EQ(v1.y, -17);
-    EXPECT_EQ(v1.z, -26);
-    EXPECT_EQ(v1.w, -35);
-}
-
-TEST_F(VecTest, ComparisonOps) {
-    vec4 v0(1,2,3,4);
-    vec4 v1(10,20,30,40);
-
-    EXPECT_TRUE(v0 == v0);
-    EXPECT_TRUE(v0 != v1);
-    EXPECT_FALSE(v0 != v0);
-    EXPECT_FALSE(v0 == v1);
-}
-
-TEST_F(VecTest, ArithmeticOps) {
-    vec4 v0(1,2,3,4);
-    vec4 v1(10,20,30,40);
-
-    vec4 v2(v0 + v1);
-    EXPECT_EQ(v2.x, 11);
-    EXPECT_EQ(v2.y, 22);
-    EXPECT_EQ(v2.z, 33);
-    EXPECT_EQ(v2.w, 44);
-
-    v0 = v1 * 2;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-
-    v0 = 2 * v1;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-
-    tvec4<double> vd(2);
-    v0 = v1 * vd;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-}
-
-TEST_F(VecTest, ArithmeticFunc) {
-    vec3 east(1, 0, 0);
-    vec3 north(0, 1, 0);
-    vec3 up( cross(east, north) );
-    EXPECT_EQ(up, vec3(0,0,1));
-    EXPECT_EQ(dot(east, north), 0);
-    EXPECT_EQ(length(east), 1);
-    EXPECT_EQ(distance(east, north), sqrtf(2));
-
-    vec3 v0(1,2,3);
-    vec3 vn(normalize(v0));
-    EXPECT_FLOAT_EQ(1, length(vn));
-    EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
-
-    tvec3<double> vd(east);
-    EXPECT_EQ(length(vd), 1);
-}
-
-}; // namespace android
diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp
new file mode 100644
index 0000000..fb46c2b
--- /dev/null
+++ b/libs/ui/tools/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libui_tools_default",
+    clang_cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_binary {
+    name: "lutgen",
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: ["libui"],
+    srcs: ["lutgen.cpp"],
+}
diff --git a/libs/ui/tools/lutgen.cpp b/libs/ui/tools/lutgen.cpp
new file mode 100644
index 0000000..97b0822
--- /dev/null
+++ b/libs/ui/tools/lutgen.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include <ui/ColorSpace.h>
+
+using namespace android;
+using namespace std;
+
+uint32_t gSize = 32;
+ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
+ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
+string gNameSrc = "DisplayP3";
+string gNameDst = "extendedSRGB";
+
+static void printHelp() {
+    cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl;
+    cout << endl;
+    cout << "Generate a 3D LUT to convert between two color spaces." << endl;
+    cout << endl;
+    cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl;
+    cout << endl;
+    cout << "Options:" << endl;
+    cout << "  --help, -h" << endl;
+    cout << "    print this message" << endl;
+    cout << "  --dimension=, -d" << endl;
+    cout << "    the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl;
+    cout << "  --source=COLORSPACE, -s" << endl;
+    cout << "    the source color space, see below for available names. DisplayP3 by default" << endl;
+    cout << "  --target=COLORSPACE, -t" << endl;
+    cout << "    the target color space, see below for available names. extendedSRGB by default" << endl;
+    cout << endl;
+    cout << "Colorspace names:" << endl;
+    cout << "    sRGB" << endl;
+    cout << "    linearSRGB" << endl;
+    cout << "    extendedSRGB" << endl;
+    cout << "    linearExtendedSRGB" << endl;
+    cout << "    NTSC" << endl;
+    cout << "    BT709" << endl;
+    cout << "    BT2020" << endl;
+    cout << "    AdobeRGB" << endl;
+    cout << "    ProPhotoRGB" << endl;
+    cout << "    DisplayP3" << endl;
+    cout << "    DCIP3" << endl;
+    cout << "    ACES" << endl;
+    cout << "    ACEScg" << endl;
+}
+
+static const ColorSpace findColorSpace(const string& name) {
+    if (name == "linearSRGB") return ColorSpace::linearSRGB();
+    if (name == "extendedSRGB") return ColorSpace::extendedSRGB();
+    if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB();
+    if (name == "NTSC") return ColorSpace::NTSC();
+    if (name == "BT709") return ColorSpace::BT709();
+    if (name == "BT2020") return ColorSpace::BT2020();
+    if (name == "AdobeRGB") return ColorSpace::AdobeRGB();
+    if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB();
+    if (name == "DisplayP3") return ColorSpace::DisplayP3();
+    if (name == "DCIP3") return ColorSpace::DCIP3();
+    if (name == "ACES") return ColorSpace::ACES();
+    if (name == "ACEScg") return ColorSpace::ACEScg();
+    return ColorSpace::sRGB();
+}
+
+static int handleCommandLineArgments(int argc, char* argv[]) {
+    static constexpr const char* OPTSTR = "h:d:s:t:";
+    static const struct option OPTIONS[] = {
+            { "help",       no_argument,       0, 'h' },
+            { "dimension",  required_argument, 0, 'd' },
+            { "source",     required_argument, 0, 's' },
+            { "target",     required_argument, 0, 't' },
+            { 0, 0, 0, 0 }  // termination of the option list
+    };
+
+    int opt;
+    int index = 0;
+
+    while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) {
+        string arg(optarg ? optarg : "");
+        switch (opt) {
+            default:
+            case 'h':
+                printHelp();
+                exit(0);
+                break;
+            case 'd':
+                gSize = max(2, min(stoi(arg), 256));
+                break;
+            case 's':
+                gNameSrc = arg;
+                gColorSpaceSrc = findColorSpace(arg);
+                break;
+            case 't':
+                gNameDst = arg;
+                gColorSpaceDst = findColorSpace(arg);
+                break;
+        }
+    }
+
+    return optind;
+}
+
+int main(int argc, char* argv[]) {
+    int optionIndex = handleCommandLineArgments(argc, argv);
+    int numArgs = argc - optionIndex;
+
+    if (numArgs < 1) {
+        printHelp();
+        return 1;
+    }
+    
+    bool isInclude = false;
+
+    string filename(argv[optionIndex]);
+    size_t index = filename.find_last_of('.');
+
+    if (index != string::npos) {
+        string extension(filename.substr(index + 1));
+        isInclude = extension == "inc";
+    }
+
+    ofstream outputStream(filename, ios::trunc);
+    if (outputStream.good()) {
+        auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst);
+        auto data = lut.get();
+
+        outputStream << "// generated with lutgen " << filename.c_str() << endl;
+        outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl;
+        outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl;
+
+        string src(gNameSrc);
+        string dst(gNameDst);
+
+        if (!isInclude) {
+            transform(src.begin(), src.end(), src.begin(), ::toupper);
+            transform(dst.begin(), dst.end(), dst.begin(), ::toupper);
+
+            outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl;
+            outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {";
+        } else {
+            outputStream << "// From " << src << " to " << dst << endl;
+        }
+
+        for (size_t z = 0; z < gSize; z++) {
+            for (size_t y = 0; y < gSize; y++) {
+                for (size_t x = 0; x < gSize; x++) {
+                    if (x % 4 == 0) outputStream << endl << "    ";
+
+                    half3 rgb = half3(*data++);
+
+                    const uint16_t r = rgb.r.getBits();
+                    const uint16_t g = rgb.g.getBits();
+                    const uint16_t b = rgb.b.getBits();
+
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", ";
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", ";
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", ";
+                }
+            }
+        }
+
+        if (!isInclude) {
+            outputStream << endl << "}; // end LUT" << endl;
+        }
+
+        outputStream << endl;
+        outputStream.flush();
+        outputStream.close();
+    } else {
+        cerr << "Could not write to file: " << filename << endl;
+        return 1;
+
+    }
+
+    return 0;
+}
diff --git a/libs/vr/.clang-format b/libs/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/libs/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp
new file mode 100644
index 0000000..e8176cf
--- /dev/null
+++ b/libs/vr/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+    "*",
+]
diff --git a/libs/vr/CPPLINT.cfg b/libs/vr/CPPLINT.cfg
new file mode 100644
index 0000000..87fb641
--- /dev/null
+++ b/libs/vr/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
new file mode 100644
index 0000000..13af470
--- /dev/null
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -0,0 +1,35 @@
+cc_library_static {
+    name: "libbroadcastring",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbase",
+    ],
+    export_shared_lib_headers: [
+        "libbase",
+    ],
+}
+
+cc_test {
+    name: "broadcast_ring_tests",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "broadcast_ring_test.cc",
+    ],
+    static_libs: [
+        "libbroadcastring",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libs/vr/libbroadcastring/broadcast_ring_test.cc b/libs/vr/libbroadcastring/broadcast_ring_test.cc
new file mode 100644
index 0000000..dfdd4ef
--- /dev/null
+++ b/libs/vr/libbroadcastring/broadcast_ring_test.cc
@@ -0,0 +1,866 @@
+#include "libbroadcastring/broadcast_ring.h"
+
+#include <stdlib.h>
+#include <memory>
+#include <thread>  // NOLINT
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+template <uint32_t N>
+struct alignas(8) Aligned {
+  char v[N];
+};
+
+template <uint32_t N>
+struct alignas(8) Sized {
+  Sized() { Clear(); }
+  explicit Sized(char c) { Fill(c); }
+  char v[sizeof(Aligned<N>)];
+  void Clear() { memset(v, 0, sizeof(v)); }
+  void Fill(char c) { memset(v, c, sizeof(v)); }
+  static Sized Pattern(uint8_t c) {
+    Sized sized;
+    for (size_t i = 0; i < sizeof(v); ++i) {
+      sized.v[i] = static_cast<char>(c + i);
+    }
+    return sized;
+  }
+  bool operator==(const Sized& right) const {
+    static_assert(sizeof(*this) == sizeof(v), "Size mismatch");
+    return !memcmp(v, right.v, sizeof(v));
+  }
+  template <typename SmallerSized>
+  SmallerSized Truncate() const {
+    SmallerSized val;
+    static_assert(sizeof(val.v) <= sizeof(v), "Cannot truncate to larger size");
+    memcpy(val.v, v, sizeof(val.v));
+    return val;
+  }
+};
+
+char FillChar(int val) { return static_cast<char>(val); }
+
+struct FakeMmap {
+  explicit FakeMmap(size_t size) : size(size), data(new char[size]) {}
+  size_t size;
+  std::unique_ptr<char[]> data;
+  void* mmap() { return static_cast<void*>(data.get()); }
+};
+
+template <typename Ring>
+FakeMmap CreateRing(Ring* ring, uint32_t count) {
+  FakeMmap mmap(Ring::MemorySize(count));
+  *ring = Ring::Create(mmap.mmap(), mmap.size, count);
+  return mmap;
+}
+
+template <typename RecordType, bool StaticSize = false,
+          uint32_t StaticCount = 0, uint32_t MaxReserved = 1,
+          uint32_t MinAvailable = 0>
+struct Traits {
+  using Record = RecordType;
+  static constexpr bool kUseStaticRecordSize = StaticSize;
+  static constexpr uint32_t kStaticRecordCount = StaticCount;
+  static constexpr uint32_t kMaxReservedRecords = MaxReserved;
+  static constexpr uint32_t kMinAvailableRecords = MinAvailable;
+  static constexpr uint32_t kMinRecordCount = MaxReserved + MinAvailable;
+};
+
+template <typename Record, bool StaticSize = false, uint32_t MaxReserved = 1,
+          uint32_t MinAvailable = 7>
+struct TraitsDynamic
+    : public Traits<Record, StaticSize, 0, MaxReserved, MinAvailable> {
+  using Ring = BroadcastRing<Record, TraitsDynamic>;
+  static uint32_t MinCount() { return MaxReserved + MinAvailable; }
+};
+
+template <typename Record, uint32_t StaticCount = 1, bool StaticSize = true,
+          uint32_t MaxReserved = 1, uint32_t MinAvailable = 0>
+struct TraitsStatic
+    : public Traits<Record, true, StaticCount, MaxReserved, MinAvailable> {
+  using Ring = BroadcastRing<Record, TraitsStatic>;
+  static uint32_t MinCount() { return StaticCount; }
+};
+
+using Dynamic_8_NxM = TraitsDynamic<Sized<8>>;
+using Dynamic_16_NxM = TraitsDynamic<Sized<16>>;
+using Dynamic_32_NxM = TraitsDynamic<Sized<32>>;
+using Dynamic_32_32xM = TraitsDynamic<Sized<32>, true>;
+using Dynamic_16_NxM_1plus0 = TraitsDynamic<Sized<16>, false, 1, 0>;
+using Dynamic_16_NxM_1plus1 = TraitsDynamic<Sized<16>, false, 1, 1>;
+using Dynamic_16_NxM_5plus11 = TraitsDynamic<Sized<16>, false, 5, 11>;
+using Dynamic_256_NxM_1plus0 = TraitsDynamic<Sized<256>, false, 1, 0>;
+
+using Static_8_8x1 = TraitsStatic<Sized<8>, 1>;
+using Static_8_8x16 = TraitsStatic<Sized<8>, 16>;
+using Static_16_16x8 = TraitsStatic<Sized<16>, 8>;
+using Static_16_16x16 = TraitsStatic<Sized<16>, 16>;
+using Static_16_16x32 = TraitsStatic<Sized<16>, 32>;
+using Static_32_Nx8 = TraitsStatic<Sized<32>, 8, false>;
+
+using TraitsList = ::testing::Types<Dynamic_8_NxM,           //
+                                    Dynamic_16_NxM,          //
+                                    Dynamic_32_NxM,          //
+                                    Dynamic_32_32xM,         //
+                                    Dynamic_16_NxM_1plus0,   //
+                                    Dynamic_16_NxM_1plus1,   //
+                                    Dynamic_16_NxM_5plus11,  //
+                                    Dynamic_256_NxM_1plus0,  //
+                                    Static_8_8x1,            //
+                                    Static_8_8x16,           //
+                                    Static_16_16x8,          //
+                                    Static_16_16x16,         //
+                                    Static_16_16x32,         //
+                                    Static_32_Nx8>;
+
+}  // namespace
+
+template <typename T>
+class BroadcastRingTest : public ::testing::Test {};
+
+TYPED_TEST_CASE(BroadcastRingTest, TraitsList);
+
+TYPED_TEST(BroadcastRingTest, Geometry) {
+  using Record = typename TypeParam::Record;
+  using Ring = typename TypeParam::Ring;
+  Ring ring;
+  auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+  EXPECT_EQ(Ring::Traits::MinCount(), ring.record_count());
+  EXPECT_EQ(sizeof(Record), ring.record_size());
+}
+
+TYPED_TEST(BroadcastRingTest, PutGet) {
+  using Record = typename TypeParam::Record;
+  using Ring = typename TypeParam::Ring;
+  Ring ring;
+  auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+  const uint32_t oldest_sequence_at_start = ring.GetOldestSequence();
+  const uint32_t next_sequence_at_start = ring.GetNextSequence();
+  {
+    uint32_t sequence = oldest_sequence_at_start;
+    Record record;
+    EXPECT_FALSE(ring.Get(&sequence, &record));
+    EXPECT_EQ(oldest_sequence_at_start, sequence);
+    EXPECT_EQ(Record(), record);
+  }
+  const Record original_record(0x1a);
+  ring.Put(original_record);
+  {
+    uint32_t sequence = next_sequence_at_start;
+    Record record;
+    EXPECT_TRUE(ring.Get(&sequence, &record));
+    EXPECT_EQ(next_sequence_at_start, sequence);
+    EXPECT_EQ(original_record, record);
+  }
+  {
+    uint32_t sequence = next_sequence_at_start + 1;
+    Record record;
+    EXPECT_FALSE(ring.Get(&sequence, &record));
+    EXPECT_EQ(next_sequence_at_start + 1, sequence);
+    EXPECT_EQ(Record(), record);
+  }
+}
+
+TYPED_TEST(BroadcastRingTest, FillOnce) {
+  using Record = typename TypeParam::Record;
+  using Ring = typename TypeParam::Ring;
+  Ring ring;
+  auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+  const uint32_t next_sequence_at_start = ring.GetNextSequence();
+  for (uint32_t i = 0; i < ring.record_count(); ++i)
+    ring.Put(Record(FillChar(i)));
+  for (uint32_t i = 0; i < ring.record_count(); ++i) {
+    const uint32_t expected_sequence = next_sequence_at_start + i;
+    const Record expected_record(FillChar(i));
+    {
+      uint32_t sequence = ring.GetOldestSequence() + i;
+      Record record;
+      EXPECT_TRUE(ring.Get(&sequence, &record));
+      EXPECT_EQ(expected_sequence, sequence);
+      EXPECT_EQ(expected_record, record);
+    }
+  }
+  {
+    uint32_t sequence = ring.GetOldestSequence() + ring.record_count();
+    Record record;
+    EXPECT_FALSE(ring.Get(&sequence, &record));
+  }
+}
+
+TYPED_TEST(BroadcastRingTest, FillTwice) {
+  using Record = typename TypeParam::Record;
+  using Ring = typename TypeParam::Ring;
+  Ring ring;
+  auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+  const uint32_t next_sequence_at_start = ring.GetNextSequence();
+  for (uint32_t i = 0; i < 2 * ring.record_count(); ++i) {
+    const Record newest_record(FillChar(i));
+    ring.Put(newest_record);
+
+    const uint32_t newest_sequence = next_sequence_at_start + i;
+    const uint32_t records_available = std::min(i + 1, ring.record_count());
+    const uint32_t oldest_sequence = newest_sequence - records_available + 1;
+    EXPECT_EQ(newest_sequence, ring.GetNewestSequence());
+    EXPECT_EQ(oldest_sequence, ring.GetOldestSequence());
+    EXPECT_EQ(newest_sequence + 1, ring.GetNextSequence());
+
+    for (uint32_t j = 0; j < records_available; ++j) {
+      const uint32_t sequence_jth_newest = newest_sequence - j;
+      const Record record_jth_newest(FillChar(i - j));
+
+      {
+        uint32_t sequence = sequence_jth_newest;
+        Record record;
+        EXPECT_TRUE(ring.Get(&sequence, &record));
+        EXPECT_EQ(sequence_jth_newest, sequence);
+        EXPECT_EQ(record_jth_newest, record);
+      }
+
+      {
+        uint32_t sequence = sequence_jth_newest;
+        Record record;
+        EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+        EXPECT_EQ(newest_sequence, sequence);
+        EXPECT_EQ(newest_record, record);
+      }
+    }
+
+    const Record oldest_record(
+        FillChar(i + (oldest_sequence - newest_sequence)));
+    const uint32_t sequence_0th_overwritten = oldest_sequence - 1;
+    const uint32_t sequence_0th_future = newest_sequence + 1;
+    const uint32_t sequence_1st_future = newest_sequence + 2;
+
+    {
+      uint32_t sequence = sequence_0th_overwritten;
+      Record record;
+      EXPECT_TRUE(ring.Get(&sequence, &record));
+      EXPECT_EQ(oldest_sequence, sequence);
+      EXPECT_EQ(oldest_record, record);
+    }
+
+    {
+      uint32_t sequence = sequence_0th_overwritten;
+      Record record;
+      EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+      EXPECT_EQ(newest_sequence, sequence);
+      EXPECT_EQ(newest_record, record);
+    }
+
+    {
+      uint32_t sequence = sequence_0th_future;
+      Record record;
+      EXPECT_FALSE(ring.Get(&sequence, &record));
+      EXPECT_EQ(sequence_0th_future, sequence);
+      EXPECT_EQ(Record(), record);
+    }
+
+    {
+      uint32_t sequence = sequence_0th_future;
+      Record record;
+      EXPECT_FALSE(ring.GetNewest(&sequence, &record));
+      EXPECT_EQ(sequence_0th_future, sequence);
+      EXPECT_EQ(Record(), record);
+    }
+
+    {
+      uint32_t sequence = sequence_1st_future;
+      Record record;
+      EXPECT_TRUE(ring.Get(&sequence, &record));
+      EXPECT_EQ(oldest_sequence, sequence);
+      EXPECT_EQ(oldest_record, record);
+    }
+
+    {
+      uint32_t sequence = sequence_1st_future;
+      Record record;
+      EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+      EXPECT_EQ(newest_sequence, sequence);
+      EXPECT_EQ(newest_record, record);
+    }
+  }
+}
+
+TYPED_TEST(BroadcastRingTest, Import) {
+  using Record = typename TypeParam::Record;
+  using Ring = typename TypeParam::Ring;
+  Ring ring;
+  auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+
+  const uint32_t sequence_0 = ring.GetNextSequence();
+  const uint32_t sequence_1 = ring.GetNextSequence() + 1;
+  const Record record_0 = Record::Pattern(0x00);
+  const Record record_1 = Record::Pattern(0x80);
+  ring.Put(record_0);
+  ring.Put(record_1);
+
+  {
+    Ring imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) = Ring::Import(mmap.mmap(), mmap.size);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(ring.record_count(), imported_ring.record_count());
+
+    if (ring.record_count() != 1) {
+      uint32_t sequence = sequence_0;
+      Record imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(sequence_0, sequence);
+      EXPECT_EQ(record_0, imported_record);
+    }
+
+    {
+      uint32_t sequence = sequence_1;
+      Record imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(sequence_1, sequence);
+      EXPECT_EQ(record_1, imported_record);
+    }
+  }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfStaticSizeMismatch) {
+  using OriginalRing = typename Static_16_16x16::Ring;
+  using RecordSizeMismatchRing = typename Static_8_8x16::Ring;
+  using RecordCountMismatchRing = typename Static_16_16x8::Ring;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+  {
+    using ImportedRing = RecordSizeMismatchRing;
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_FALSE(import_ok);
+    auto mmap_imported =
+        CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+    EXPECT_NE(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+  }
+
+  {
+    using ImportedRing = RecordCountMismatchRing;
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_FALSE(import_ok);
+    auto mmap_imported =
+        CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_NE(original_ring.record_count(), imported_ring.record_count());
+  }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfDynamicSizeGrows) {
+  using OriginalRing = typename Dynamic_8_NxM::Ring;
+  using RecordSizeGrowsRing = typename Dynamic_16_NxM::Ring;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+  {
+    using ImportedRing = RecordSizeGrowsRing;
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_FALSE(import_ok);
+    auto mmap_imported =
+        CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+    EXPECT_LT(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+  }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfCountTooSmall) {
+  using OriginalRing = typename Dynamic_16_NxM_1plus0::Ring;
+  using MinCountRing = typename Dynamic_16_NxM_1plus1::Ring;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+  {
+    using ImportedRing = MinCountRing;
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_FALSE(import_ok);
+    auto mmap_imported =
+        CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_LT(original_ring.record_count(), imported_ring.record_count());
+  }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfMmapTooSmall) {
+  using OriginalRing = typename Dynamic_16_NxM::Ring;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+  {
+    using ImportedRing = OriginalRing;
+    ImportedRing imported_ring;
+    bool import_ok;
+    const size_t kMinSize =
+        ImportedRing::MemorySize(original_ring.record_count());
+    std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), 0);
+    EXPECT_FALSE(import_ok);
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), kMinSize - 1);
+    EXPECT_FALSE(import_ok);
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), kMinSize);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+  }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfDynamicSizeShrinks) {
+  using OriginalRing = typename Dynamic_16_NxM::Ring;
+  using RecordSizeShrinksRing = typename Dynamic_8_NxM::Ring;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+  using OriginalRecord = typename OriginalRing::Record;
+  const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+  const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+  const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+  const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+  original_ring.Put(original_record_0);
+  original_ring.Put(original_record_1);
+
+  {
+    using ImportedRing = RecordSizeShrinksRing;
+    using ImportedRecord = typename ImportedRing::Record;
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+    EXPECT_GT(sizeof(OriginalRecord), sizeof(ImportedRecord));
+
+    {
+      uint32_t sequence = original_sequence_0;
+      ImportedRecord shrunk_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
+      EXPECT_EQ(original_sequence_0, sequence);
+      EXPECT_EQ(original_record_0.Truncate<ImportedRecord>(), shrunk_record);
+    }
+
+    {
+      uint32_t sequence = original_sequence_1;
+      ImportedRecord shrunk_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
+      EXPECT_EQ(original_sequence_1, sequence);
+      EXPECT_EQ(original_record_1.Truncate<ImportedRecord>(), shrunk_record);
+    }
+  }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfCompatibleDynamicToStatic) {
+  using OriginalRing = typename Dynamic_16_NxM::Ring;
+  using ImportedRing = typename Static_16_16x16::Ring;
+  using OriginalRecord = typename OriginalRing::Record;
+  using ImportedRecord = typename ImportedRing::Record;
+  using StaticRing = ImportedRing;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
+
+  const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+  const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+  const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+  const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+  original_ring.Put(original_record_0);
+  original_ring.Put(original_record_1);
+
+  {
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+
+    {
+      uint32_t sequence = original_sequence_0;
+      ImportedRecord imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(original_sequence_0, sequence);
+      EXPECT_EQ(original_record_0, imported_record);
+    }
+
+    {
+      uint32_t sequence = original_sequence_1;
+      ImportedRecord imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(original_sequence_1, sequence);
+      EXPECT_EQ(original_record_1, imported_record);
+    }
+  }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfCompatibleStaticToDynamic) {
+  using OriginalRing = typename Static_16_16x16::Ring;
+  using ImportedRing = typename Dynamic_16_NxM::Ring;
+  using OriginalRecord = typename OriginalRing::Record;
+  using ImportedRecord = typename ImportedRing::Record;
+  using StaticRing = OriginalRing;
+
+  OriginalRing original_ring;
+  auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
+
+  const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+  const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+  const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+  const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+  original_ring.Put(original_record_0);
+  original_ring.Put(original_record_1);
+
+  {
+    ImportedRing imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) =
+        ImportedRing::Import(mmap.mmap(), mmap.size);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+
+    {
+      uint32_t sequence = original_sequence_0;
+      ImportedRecord imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(original_sequence_0, sequence);
+      EXPECT_EQ(original_record_0, imported_record);
+    }
+
+    {
+      uint32_t sequence = original_sequence_1;
+      ImportedRecord imported_record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+      EXPECT_EQ(original_sequence_1, sequence);
+      EXPECT_EQ(original_record_1, imported_record);
+    }
+  }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfReadonlyMmap) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+
+  uint32_t record_count = Ring::Traits::MinCount();
+  size_t ring_size = Ring::MemorySize(record_count);
+
+  size_t page_size = sysconf(_SC_PAGESIZE);
+  size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+  ASSERT_GE(mmap_size, ring_size);
+
+  void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
+                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mmap_base);
+
+  Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+  for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
+
+  ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
+
+  {
+    Ring imported_ring;
+    bool import_ok;
+    std::tie(imported_ring, import_ok) = Ring::Import(mmap_base, mmap_size);
+    EXPECT_TRUE(import_ok);
+    EXPECT_EQ(ring.record_size(), imported_ring.record_size());
+    EXPECT_EQ(ring.record_count(), imported_ring.record_count());
+
+    uint32_t oldest_sequence = imported_ring.GetOldestSequence();
+    for (uint32_t i = 0; i < record_count; ++i) {
+      uint32_t sequence = oldest_sequence + i;
+      Record record;
+      EXPECT_TRUE(imported_ring.Get(&sequence, &record));
+      EXPECT_EQ(Record(FillChar(i)), record);
+    }
+  }
+
+  ASSERT_EQ(0, munmap(mmap_base, mmap_size));
+}
+
+TEST(BroadcastRingTest, ShouldDieIfPutReadonlyMmap) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+
+  uint32_t record_count = Ring::Traits::MinCount();
+  size_t ring_size = Ring::MemorySize(record_count);
+
+  size_t page_size = sysconf(_SC_PAGESIZE);
+  size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+  ASSERT_GE(mmap_size, ring_size);
+
+  void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
+                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mmap_base);
+
+  Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+  for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
+
+  ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
+
+  EXPECT_DEATH_IF_SUPPORTED({ ring.Put(Record(7)); }, "");
+
+  ASSERT_EQ(0, munmap(mmap_base, mmap_size));
+}
+
+TEST(BroadcastRingTest, ShouldDieIfCreationMmapTooSmall) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+
+  uint32_t record_count = Ring::Traits::MinCount();
+  size_t ring_size = Ring::MemorySize(record_count);
+  FakeMmap mmap(ring_size);
+
+  EXPECT_DEATH_IF_SUPPORTED({
+    Ring ring = Ring::Create(mmap.mmap(), ring_size - 1, record_count);
+  }, "");
+
+  Ring ring = Ring::Create(mmap.mmap(), ring_size, record_count);
+
+  ring.Put(Record(3));
+
+  {
+    uint32_t sequence = ring.GetNewestSequence();
+    Record record;
+    EXPECT_TRUE(ring.Get(&sequence, &record));
+    EXPECT_EQ(Record(3), record);
+  }
+}
+
+TEST(BroadcastRingTest, ShouldDieIfCreationMmapMisaligned) {
+  using Ring = Static_8_8x1::Ring;
+  using Record = Ring::Record;
+
+  constexpr int kAlign = Ring::mmap_alignment();
+  constexpr int kMisalign = kAlign / 2;
+  size_t ring_size = Ring::MemorySize();
+  std::unique_ptr<char[]> buf(new char[ring_size + kMisalign]);
+
+  EXPECT_DEATH_IF_SUPPORTED(
+      { Ring ring = Ring::Create(buf.get() + kMisalign, ring_size); }, "");
+
+  Ring ring = Ring::Create(buf.get(), ring_size);
+
+  ring.Put(Record(3));
+
+  {
+    uint32_t sequence = ring.GetNewestSequence();
+    Record record;
+    EXPECT_TRUE(ring.Get(&sequence, &record));
+    EXPECT_EQ(Record(3), record);
+  }
+}
+
+template <typename Ring>
+std::unique_ptr<std::thread> CopyTask(std::atomic<bool>* quit, void* in_base,
+                                      size_t in_size, void* out_base,
+                                      size_t out_size) {
+  return std::unique_ptr<std::thread>(
+      new std::thread([quit, in_base, in_size, out_base, out_size]() {
+        using Record = typename Ring::Record;
+
+        bool import_ok;
+        Ring in_ring;
+        Ring out_ring;
+        std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
+        ASSERT_TRUE(import_ok);
+        std::tie(out_ring, import_ok) = Ring::Import(out_base, out_size);
+        ASSERT_TRUE(import_ok);
+
+        uint32_t sequence = in_ring.GetOldestSequence();
+        while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
+          Record record;
+          if (in_ring.Get(&sequence, &record)) {
+            out_ring.Put(record);
+            sequence++;
+          }
+        }
+      }));
+}
+
+TEST(BroadcastRingTest, ThreadedCopySingle) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+  Ring in_ring;
+  auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+  Ring out_ring;
+  auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+  std::atomic<bool> quit(false);
+  std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+      &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+  const Record out_record(0x1c);
+  out_ring.Put(out_record);
+
+  uint32_t in_sequence = in_ring.GetOldestSequence();
+  Record in_record;
+  while (!in_ring.Get(&in_sequence, &in_record)) {
+    // Do nothing.
+  }
+
+  EXPECT_EQ(out_record, in_record);
+  std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+  copy_task->join();
+}
+
+TEST(BroadcastRingTest, ThreadedCopyLossless) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+  Ring in_ring;
+  auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+  Ring out_ring;
+  auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+  std::atomic<bool> quit(false);
+  std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+      &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+  constexpr uint32_t kRecordsToProcess = 10000;
+  uint32_t out_records = 0;
+  uint32_t in_records = 0;
+  uint32_t in_sequence = in_ring.GetNextSequence();
+  while (out_records < kRecordsToProcess || in_records < kRecordsToProcess) {
+    if (out_records < kRecordsToProcess &&
+        out_records - in_records < out_ring.record_count()) {
+      const Record out_record(FillChar(out_records));
+      out_ring.Put(out_record);
+      out_records++;
+    }
+
+    Record in_record;
+    while (in_ring.Get(&in_sequence, &in_record)) {
+      EXPECT_EQ(Record(FillChar(in_records)), in_record);
+      in_records++;
+      in_sequence++;
+    }
+  }
+
+  EXPECT_EQ(kRecordsToProcess, out_records);
+  EXPECT_EQ(kRecordsToProcess, in_records);
+
+  std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+  copy_task->join();
+}
+
+TEST(BroadcastRingTest, ThreadedCopyLossy) {
+  using Ring = Dynamic_32_NxM::Ring;
+  using Record = Ring::Record;
+  Ring in_ring;
+  auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+  Ring out_ring;
+  auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+  std::atomic<bool> quit(false);
+  std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+      &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+  constexpr uint32_t kRecordsToProcess = 100000;
+  uint32_t out_records = 0;
+  uint32_t in_records = 0;
+  uint32_t in_sequence = in_ring.GetNextSequence();
+  while (out_records < kRecordsToProcess) {
+    const Record out_record(FillChar(out_records));
+    out_ring.Put(out_record);
+    out_records++;
+
+    Record in_record;
+    if (in_ring.GetNewest(&in_sequence, &in_record)) {
+      EXPECT_EQ(Record(in_record.v[0]), in_record);
+      in_records++;
+      in_sequence++;
+    }
+  }
+
+  EXPECT_EQ(kRecordsToProcess, out_records);
+  EXPECT_GE(kRecordsToProcess, in_records);
+
+  std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+  copy_task->join();
+}
+
+template <typename Ring>
+std::unique_ptr<std::thread> CheckFillTask(std::atomic<bool>* quit,
+                                           void* in_base, size_t in_size) {
+  return std::unique_ptr<std::thread>(
+      new std::thread([quit, in_base, in_size]() {
+        using Record = typename Ring::Record;
+
+        bool import_ok;
+        Ring in_ring;
+        std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
+        ASSERT_TRUE(import_ok);
+
+        uint32_t sequence = in_ring.GetOldestSequence();
+        while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
+          Record record;
+          if (in_ring.Get(&sequence, &record)) {
+            ASSERT_EQ(Record(record.v[0]), record);
+            sequence++;
+          }
+        }
+      }));
+}
+
+template <typename Ring>
+void ThreadedOverwriteTorture() {
+  using Record = typename Ring::Record;
+
+  // Maximize overwrites by having few records.
+  const int kMinRecordCount = 1;
+  const int kMaxRecordCount = 4;
+
+  for (int count = kMinRecordCount; count <= kMaxRecordCount; count *= 2) {
+    Ring out_ring;
+    auto out_mmap = CreateRing(&out_ring, count);
+
+    std::atomic<bool> quit(false);
+    std::unique_ptr<std::thread> check_task =
+        CheckFillTask<Ring>(&quit, out_mmap.mmap(), out_mmap.size);
+
+    constexpr int kIterations = 10000;
+    for (int i = 0; i < kIterations; ++i) {
+      const Record record(FillChar(i));
+      out_ring.Put(record);
+    }
+
+    std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+    check_task->join();
+  }
+}
+
+TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) {
+  ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>();
+}
+
+TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) {
+  ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
new file mode 100644
index 0000000..69cb648
--- /dev/null
+++ b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
@@ -0,0 +1,668 @@
+#ifndef ANDROID_DVR_BROADCAST_RING_H_
+#define ANDROID_DVR_BROADCAST_RING_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <atomic>
+#include <limits>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "android-base/logging.h"
+
+#if ATOMIC_LONG_LOCK_FREE != 2 || ATOMIC_INT_LOCK_FREE != 2
+#error "This file requires lock free atomic uint32_t and long"
+#endif
+
+namespace android {
+namespace dvr {
+
+struct DefaultRingTraits {
+  // Set this to false to allow compatibly expanding the record size.
+  static constexpr bool kUseStaticRecordSize = false;
+
+  // Set this to a nonzero value to fix the number of records in the ring.
+  static constexpr uint32_t kStaticRecordCount = 0;
+
+  // Set this to the max number of records that can be written simultaneously.
+  static constexpr uint32_t kMaxReservedRecords = 1;
+
+  // Set this to the min number of records that must be readable.
+  static constexpr uint32_t kMinAvailableRecords = 1;
+};
+
+// Nonblocking ring suitable for concurrent single-writer, multi-reader access.
+//
+// Readers never block the writer and thus this is a nondeterministically lossy
+// transport in the absence of external synchronization. Don't use this as a
+// transport when deterministic behavior is required.
+//
+// Readers may have a read-only mapping; each reader's state is a single local
+// sequence number.
+//
+// The implementation takes care to avoid data races on record access.
+// Inconsistent data can only be returned if at least 2^32 records are written
+// during the read-side critical section.
+//
+// In addition, both readers and the writer are careful to avoid accesses
+// outside the bounds of the mmap area passed in during initialization even if
+// there is a misbehaving or malicious task with write access to the mmap area.
+//
+// When dynamic record size is enabled, readers use the record size in the ring
+// header when indexing the ring, so that it is possible to extend the record
+// type without breaking the read-side ABI.
+//
+// Avoid calling Put() in a tight loop; there should be significantly more time
+// between successive puts than it takes to read one record from memory to
+// ensure Get() completes quickly. This requirement should not be difficult to
+// achieve for most practical uses; 4kB puts at 10,000Hz is well below the
+// scaling limit on current mobile chips.
+//
+// Example Writer Usage:
+//
+//   using Record = MyRecordType;
+//   using Ring = BroadcastRing<Record>;
+//
+//   uint32_t record_count = kMyDesiredCount;
+//   uint32_t ring_size = Ring::MemorySize(record_count);
+//
+//   size_t page_size = sysconf(_SC_PAGESIZE);
+//   uint32_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+//
+//   // Allocate & map via your preferred mechanism, e.g.
+//   int fd = open("/dev/shm/ring_test", O_CREAT|O_RDWR|O_CLOEXEC, 0600);
+//   CHECK(fd >= 0);
+//   CHECK(!ftruncate(fd, ring_size));
+//   void *mmap_base = mmap(nullptr, mmap_size, PROT_READ|PROT_WRITE,
+//                          MAP_SHARED, fd, 0);
+//   CHECK(mmap_base != MAP_FAILED);
+//   close(fd);
+//
+//   Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+//
+//   while (!done)
+//     ring.Put(BuildNextRecordBlocking());
+//
+//   CHECK(!munmap(mmap_base, mmap_size));
+//
+// Example Reader Usage:
+//
+//   using Record = MyRecordType;
+//   using Ring = BroadcastRing<Record>;
+//
+//   // Map via your preferred mechanism, e.g.
+//   int fd = open("/dev/shm/ring_test", O_RDONLY|O_CLOEXEC);
+//   CHECK(fd >= 0);
+//   struct stat st;
+//   CHECK(!fstat(fd, &st));
+//   size_t mmap_size = st.st_size;
+//   void *mmap_base = mmap(nullptr, mmap_size, PROT_READ,
+//                          MAP_SHARED, fd, 0);
+//   CHECK(mmap_base != MAP_FAILED);
+//   close(fd);
+//
+//   Ring ring;
+//   bool import_ok;
+//   std::tie(ring, import_ok) = Ring::Import(mmap_base, mmap_size);
+//   CHECK(import_ok);
+//
+//   uint32_t sequence;
+//
+//   // Choose starting point (using "0" is unpredictable but not dangerous)
+//   sequence = ring.GetOldestSequence();  // The oldest available
+//   sequence = ring.GetNewestSequence();  // The newest available
+//   sequence = ring.GetNextSequence();    // The next one produced
+//
+//   while (!done) {
+//     Record record;
+//
+//     if (you_want_to_process_all_available_records) {
+//       while (ring.Get(&sequence, &record)) {
+//         ProcessRecord(sequence, record);
+//         sequence++;
+//       }
+//     } else if (you_want_to_skip_to_the_newest_record) {
+//       if (ring.GetNewest(&sequence, &record)) {
+//         ProcessRecord(sequence, record);
+//         sequence++;
+//       }
+//     }
+//
+//     DoSomethingExpensiveOrBlocking();
+//   }
+//
+//   CHECK(!munmap(mmap_base, mmap_size));
+//
+template <typename RecordType, typename BaseTraits = DefaultRingTraits>
+class BroadcastRing {
+ public:
+  using Record = RecordType;
+  struct Traits : public BaseTraits {
+    // Must have enough space for writers, plus enough space for readers.
+    static constexpr int kMinRecordCount =
+        BaseTraits::kMaxReservedRecords + BaseTraits::kMinAvailableRecords;
+
+    // Count of zero means dynamic, non-zero means static.
+    static constexpr bool kUseStaticRecordCount =
+        (BaseTraits::kStaticRecordCount != 0);
+
+    // If both record size and count are static then the overall size is too.
+    static constexpr bool kIsStaticSize =
+        BaseTraits::kUseStaticRecordSize && kUseStaticRecordCount;
+  };
+
+  static constexpr bool IsPowerOfTwo(uint32_t size) {
+    return (size & (size - 1)) == 0;
+  }
+
+  // Sanity check the options provided in Traits.
+  static_assert(Traits::kMinRecordCount >= 1, "Min record count too small");
+  static_assert(!Traits::kUseStaticRecordCount ||
+                    Traits::kStaticRecordCount >= Traits::kMinRecordCount,
+                "Static record count is too small");
+  static_assert(!Traits::kStaticRecordCount ||
+                    IsPowerOfTwo(Traits::kStaticRecordCount),
+                "Static record count is not a power of two");
+  static_assert(std::is_standard_layout<Record>::value,
+                "Record type must be standard layout");
+
+  BroadcastRing() {}
+
+  // Creates a new ring at |mmap| with |record_count| records.
+  //
+  // There must be at least |MemorySize(record_count)| bytes of space already
+  // allocated at |mmap|. The ring does not take ownership.
+  //
+  // Use this function for dynamically sized rings.
+  static BroadcastRing Create(void* mmap, size_t mmap_size,
+                              uint32_t record_count) {
+    BroadcastRing ring(mmap);
+    CHECK(ring.ValidateGeometry(mmap_size, sizeof(Record), record_count));
+    ring.InitializeHeader(sizeof(Record), record_count);
+    return ring;
+  }
+
+  // Creates a new ring at |mmap|.
+  //
+  // There must be at least |MemorySize()| bytes of space already allocated at
+  // |mmap|. The ring does not take ownership.
+  //
+  // Use this function for statically sized rings.
+  static BroadcastRing Create(void* mmap, size_t mmap_size) {
+    static_assert(Traits::kUseStaticRecordCount,
+                  "Wrong Create() function called for dynamic record count");
+    return Create(mmap, mmap_size, Traits::kStaticRecordCount);
+  }
+
+  // Imports an existing ring at |mmap|.
+  //
+  // Import may fail if the ring parameters in the mmap header are not sensible.
+  // In this case the returned boolean is false; make sure to check this value.
+  static std::tuple<BroadcastRing, bool> Import(void* mmap, size_t mmap_size) {
+    BroadcastRing ring(mmap);
+    uint32_t record_size = 0;
+    uint32_t record_count = 0;
+    if (mmap_size >= sizeof(Header)) {
+      record_size = std::atomic_load_explicit(&ring.header_mmap()->record_size,
+                                              std::memory_order_relaxed);
+      record_count = std::atomic_load_explicit(
+          &ring.header_mmap()->record_count, std::memory_order_relaxed);
+    }
+    bool ok = ring.ValidateGeometry(mmap_size, record_size, record_count);
+    return std::make_tuple(ring, ok);
+  }
+
+  ~BroadcastRing() {}
+
+  // Calculates the space necessary for a ring of size |record_count|.
+  //
+  // Use this function for dynamically sized rings.
+  static constexpr size_t MemorySize(uint32_t record_count) {
+    return sizeof(Header) + sizeof(Record) * record_count;
+  }
+
+  // Calculates the space necessary for a statically sized ring.
+  //
+  // Use this function for statically sized rings.
+  static constexpr size_t MemorySize() {
+    static_assert(
+        Traits::kUseStaticRecordCount,
+        "Wrong MemorySize() function called for dynamic record count");
+    return MemorySize(Traits::kStaticRecordCount);
+  }
+
+  // Writes a record to the ring.
+  //
+  // The oldest record is overwritten unless the ring is not already full.
+  void Put(const Record& record) {
+    const int kRecordCount = 1;
+    Reserve(kRecordCount);
+    Geometry geometry = GetGeometry();
+    PutRecordInternal(&record, record_mmap_writer(geometry.tail_index));
+    Publish(kRecordCount);
+  }
+
+  // Gets sequence number of the oldest currently available record.
+  uint32_t GetOldestSequence() const {
+    return std::atomic_load_explicit(&header_mmap()->head,
+                                     std::memory_order_relaxed);
+  }
+
+  // Gets sequence number of the first future record.
+  //
+  // If the returned value is passed to Get() and there is no concurrent Put(),
+  // Get() will return false.
+  uint32_t GetNextSequence() const {
+    return std::atomic_load_explicit(&header_mmap()->tail,
+                                     std::memory_order_relaxed);
+  }
+
+  // Gets sequence number of the newest currently available record.
+  uint32_t GetNewestSequence() const { return GetNextSequence() - 1; }
+
+  // Copies the oldest available record with sequence at least |*sequence| to
+  // |record|.
+  //
+  // Returns false if there is no recent enough record available.
+  //
+  // Updates |*sequence| with the sequence number of the record returned. To get
+  // the following record, increment this number by one.
+  //
+  // This function synchronizes with two other operations:
+  //
+  //    (1) Load-Acquire of |tail|
+  //
+  //        Together with the store-release in Publish(), this load-acquire
+  //        ensures each store to a record in PutRecordInternal() happens-before
+  //        any corresponding load in GetRecordInternal().
+  //
+  //        i.e. the stores for the records with sequence numbers < |tail| have
+  //        completed from our perspective
+  //
+  //    (2) Acquire Fence between record access & final load of |head|
+  //
+  //        Together with the release fence in Reserve(), this ensures that if
+  //        GetRecordInternal() loads a value stored in some execution of
+  //        PutRecordInternal(), then the store of |head| in the Reserve() that
+  //        preceeded it happens-before our final load of |head|.
+  //
+  //        i.e. if we read a record with sequence number >= |final_head| then
+  //        no later store to that record has completed from our perspective
+  bool Get(uint32_t* sequence /*inout*/, Record* record /*out*/) const {
+    for (;;) {
+      uint32_t tail = std::atomic_load_explicit(&header_mmap()->tail,
+                                                std::memory_order_acquire);
+      uint32_t head = std::atomic_load_explicit(&header_mmap()->head,
+                                                std::memory_order_relaxed);
+
+      if (tail - head > record_count())
+        continue;  // Concurrent modification; re-try.
+
+      if (*sequence - head > tail - head)
+        *sequence = head;  // Out of window, skip forward to first available.
+
+      if (*sequence == tail) return false;  // No new records available.
+
+      Geometry geometry =
+          CalculateGeometry(record_count(), record_size(), *sequence, tail);
+
+      // Compute address explicitly in case record_size > sizeof(Record).
+      RecordStorage* record_storage = record_mmap_reader(geometry.head_index);
+
+      GetRecordInternal(record_storage, record);
+
+      // NB: It is not sufficient to change this to a load-acquire of |head|.
+      std::atomic_thread_fence(std::memory_order_acquire);
+
+      uint32_t final_head = std::atomic_load_explicit(
+          &header_mmap()->head, std::memory_order_relaxed);
+
+      if (final_head - head > *sequence - head)
+        continue;  // Concurrent modification; re-try.
+
+      // Note: Combining the above 4 comparisons gives:
+      // 0 <= final_head - head <= sequence - head < tail - head <= record_count
+      //
+      // We can also write this as:
+      // head <=* final_head <=* sequence <* tail <=* head + record_count
+      //
+      // where <* orders by difference from head: x <* y if x - head < y - head.
+      // This agrees with the order of sequence updates during "put" operations.
+      return true;
+    }
+  }
+
+  // Copies the newest available record with sequence at least |*sequence| to
+  // |record|.
+  //
+  // Returns false if there is no recent enough record available.
+  //
+  // Updates |*sequence| with the sequence number of the record returned. To get
+  // the following record, increment this number by one.
+  bool GetNewest(uint32_t* sequence, Record* record) const {
+    uint32_t newest_sequence = GetNewestSequence();
+    if (*sequence == newest_sequence + 1) return false;
+    *sequence = newest_sequence;
+    return Get(sequence, record);
+  }
+
+  uint32_t record_count() const { return record_count_internal(); }
+  uint32_t record_size() const { return record_size_internal(); }
+  static constexpr uint32_t mmap_alignment() { return alignof(Mmap); }
+
+ private:
+  struct Header {
+    // Record size for reading out of the ring. Writers always write the full
+    // length; readers may need to read a prefix of each record.
+    std::atomic<uint32_t> record_size;
+
+    // Number of records in the ring.
+    std::atomic<uint32_t> record_count;
+
+    // Readable region is [head % record_count, tail % record_count).
+    //
+    // The region in [tail % record_count, head % record_count) was either never
+    // populated or is being updated.
+    //
+    // These are sequences numbers, not indexes - indexes should be computed
+    // with a modulus.
+    //
+    // To ensure consistency:
+    //
+    // (1) Writes advance |head| past any updated records before writing to
+    //     them, and advance |tail| after they are written.
+    // (2) Readers check |tail| before reading data and |head| after,
+    //     making sure to discard any data that was written to concurrently.
+    std::atomic<uint32_t> head;
+    std::atomic<uint32_t> tail;
+  };
+
+  // Store using the standard word size.
+  using StorageType = long;  // NOLINT
+
+  // Always require 8 byte alignment so that the same record sizes are legal on
+  // 32 and 64 bit builds.
+  static constexpr size_t kRecordAlignment = 8;
+  static_assert(kRecordAlignment % sizeof(StorageType) == 0,
+                "Bad record alignment");
+
+  struct RecordStorage {
+    // This is accessed with relaxed atomics to prevent data races on the
+    // contained data, which would be undefined behavior.
+    std::atomic<StorageType> data[sizeof(Record) / sizeof(StorageType)];
+  };
+
+  static_assert(sizeof(StorageType) *
+                        std::extent<decltype(RecordStorage::data)>() ==
+                    sizeof(Record),
+                "Record length must be a multiple of sizeof(StorageType)");
+
+  struct Geometry {
+    // Static geometry.
+    uint32_t record_count;
+    uint32_t record_size;
+
+    // Copy of atomic sequence counts.
+    uint32_t head;
+    uint32_t tail;
+
+    // First index of readable region.
+    uint32_t head_index;
+
+    // First index of writable region.
+    uint32_t tail_index;
+
+    // Number of records in readable region.
+    uint32_t count;
+
+    // Number of records in writable region.
+    uint32_t space;
+  };
+
+  // Mmap area layout.
+  //
+  // Readers should not index directly into |records| as this is not valid when
+  // dynamic record sizes are used; use record_mmap_reader() instead.
+  struct Mmap {
+    Header header;
+    RecordStorage records[];
+  };
+
+  static_assert(std::is_standard_layout<Mmap>::value,
+                "Mmap must be standard layout");
+  static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
+                "Lockless atomics contain extra state");
+  static_assert(sizeof(std::atomic<StorageType>) == sizeof(StorageType),
+                "Lockless atomics contain extra state");
+
+  explicit BroadcastRing(void* mmap) {
+    CHECK_EQ(0U, reinterpret_cast<uintptr_t>(mmap) % alignof(Mmap));
+    data_.mmap = reinterpret_cast<Mmap*>(mmap);
+  }
+
+  // Initializes the mmap area header for a new ring.
+  void InitializeHeader(uint32_t record_size, uint32_t record_count) {
+    constexpr uint32_t kInitialSequence = -256;  // Force an early wrap.
+    std::atomic_store_explicit(&header_mmap()->record_size, record_size,
+                               std::memory_order_relaxed);
+    std::atomic_store_explicit(&header_mmap()->record_count, record_count,
+                               std::memory_order_relaxed);
+    std::atomic_store_explicit(&header_mmap()->head, kInitialSequence,
+                               std::memory_order_relaxed);
+    std::atomic_store_explicit(&header_mmap()->tail, kInitialSequence,
+                               std::memory_order_relaxed);
+  }
+
+  // Validates ring geometry.
+  //
+  // Ring geometry is validated carefully on import and then cached. This allows
+  // us to avoid out-of-range accesses even if the parameters in the header are
+  // later changed.
+  bool ValidateGeometry(size_t mmap_size, uint32_t header_record_size,
+                        uint32_t header_record_count) {
+    set_record_size(header_record_size);
+    set_record_count(header_record_count);
+
+    if (record_size() != header_record_size) return false;
+    if (record_count() != header_record_count) return false;
+    if (record_count() < Traits::kMinRecordCount) return false;
+    if (record_size() < sizeof(Record)) return false;
+    if (record_size() % kRecordAlignment != 0) return false;
+    if (!IsPowerOfTwo(record_count())) return false;
+
+    size_t memory_size = record_count() * record_size();
+    if (memory_size / record_size() != record_count()) return false;
+    if (memory_size + sizeof(Header) < memory_size) return false;
+    if (memory_size + sizeof(Header) > mmap_size) return false;
+
+    return true;
+  }
+
+  // Copies a record into the ring.
+  //
+  // This is done with relaxed atomics because otherwise it is racy according to
+  // the C++ memory model. This is very low overhead once optimized.
+  static inline void PutRecordInternal(const Record* in, RecordStorage* out) {
+    StorageType data[sizeof(Record) / sizeof(StorageType)];
+    memcpy(data, in, sizeof(*in));
+    for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) {
+      std::atomic_store_explicit(&out->data[i], data[i],
+                                 std::memory_order_relaxed);
+    }
+  }
+
+  // Copies a record out of the ring.
+  //
+  // This is done with relaxed atomics because otherwise it is racy according to
+  // the C++ memory model. This is very low overhead once optimized.
+  static inline void GetRecordInternal(RecordStorage* in, Record* out) {
+    StorageType data[sizeof(Record) / sizeof(StorageType)];
+    for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) {
+      data[i] =
+          std::atomic_load_explicit(&in->data[i], std::memory_order_relaxed);
+    }
+    memcpy(out, &data, sizeof(*out));
+  }
+
+  // Converts a record's sequence number into a storage index.
+  static uint32_t SequenceToIndex(uint32_t sequence, uint32_t record_count) {
+    return sequence & (record_count - 1);
+  }
+
+  // Computes readable & writable ranges from ring parameters.
+  static Geometry CalculateGeometry(uint32_t record_count, uint32_t record_size,
+                                    uint32_t head, uint32_t tail) {
+    Geometry geometry;
+    geometry.record_count = record_count;
+    geometry.record_size = record_size;
+    DCHECK_EQ(0U, geometry.record_size % kRecordAlignment);
+    geometry.head = head;
+    geometry.tail = tail;
+    geometry.head_index = SequenceToIndex(head, record_count);
+    geometry.tail_index = SequenceToIndex(tail, record_count);
+    geometry.count = geometry.tail - geometry.head;
+    DCHECK_LE(geometry.count, record_count);
+    geometry.space = geometry.record_count - geometry.count;
+    return geometry;
+  }
+
+  // Gets the current ring readable & writable regions.
+  //
+  // This this is always safe from the writing thread since it is the only
+  // thread allowed to update the header.
+  Geometry GetGeometry() const {
+    return CalculateGeometry(
+        record_count(), record_size(),
+        std::atomic_load_explicit(&header_mmap()->head,
+                                  std::memory_order_relaxed),
+        std::atomic_load_explicit(&header_mmap()->tail,
+                                  std::memory_order_relaxed));
+  }
+
+  // Makes space for at least |reserve_count| records.
+  //
+  // There is nothing to prevent overwriting records that have concurrent
+  // readers. We do however ensure that this situation can be detected: the
+  // fence ensures the |head| update will be the first update seen by readers,
+  // and readers check this value after reading and discard data that may have
+  // been concurrently modified.
+  void Reserve(uint32_t reserve_count) {
+    Geometry geometry = GetGeometry();
+    DCHECK_LE(reserve_count, Traits::kMaxReservedRecords);
+    uint32_t needed =
+        (geometry.space >= reserve_count ? 0 : reserve_count - geometry.space);
+
+    std::atomic_store_explicit(&header_mmap()->head, geometry.head + needed,
+                               std::memory_order_relaxed);
+
+    // NB: It is not sufficient to change this to a store-release of |head|.
+    std::atomic_thread_fence(std::memory_order_release);
+  }
+
+  // Makes |publish_count| records visible to readers.
+  //
+  // Space must have been reserved by a previous call to Reserve().
+  void Publish(uint32_t publish_count) {
+    Geometry geometry = GetGeometry();
+    DCHECK_LE(publish_count, geometry.space);
+    std::atomic_store_explicit(&header_mmap()->tail,
+                               geometry.tail + publish_count,
+                               std::memory_order_release);
+  }
+
+  // Helpers to compute addresses in mmap area.
+  Mmap* mmap() const { return data_.mmap; }
+  Header* header_mmap() const { return &data_.mmap->header; }
+  RecordStorage* record_mmap_writer(uint32_t index) const {
+    DCHECK_EQ(sizeof(Record), record_size());
+    return &data_.mmap->records[index];
+  }
+  RecordStorage* record_mmap_reader(uint32_t index) const {
+    if (Traits::kUseStaticRecordSize) {
+      return &data_.mmap->records[index];
+    } else {
+      // Calculate the location of a record in the ring without assuming that
+      // sizeof(Record) == record_size.
+      return reinterpret_cast<RecordStorage*>(
+          reinterpret_cast<char*>(data_.mmap->records) + index * record_size());
+    }
+  }
+
+  // The following horrifying template gunk enables us to store just the mmap
+  // base pointer for compile-time statically sized rings. Dynamically sized
+  // rings also store the validated copy of the record size & count.
+  //
+  // This boils down to: use a compile time constant if available, and otherwise
+  // load the value that was validated on import from a member variable.
+  template <typename T = Traits>
+  typename std::enable_if<T::kUseStaticRecordSize, uint32_t>::type
+  record_size_internal() const {
+    return sizeof(Record);
+  }
+
+  template <typename T = Traits>
+  typename std::enable_if<!T::kUseStaticRecordSize, uint32_t>::type
+  record_size_internal() const {
+    return data_.record_size;
+  }
+
+  template <typename T = Traits>
+  typename std::enable_if<T::kUseStaticRecordSize, void>::type set_record_size(
+      uint32_t /*record_size*/) {}
+
+  template <typename T = Traits>
+  typename std::enable_if<!T::kUseStaticRecordSize, void>::type set_record_size(
+      uint32_t record_size) {
+    data_.record_size = record_size;
+  }
+
+  template <typename T = Traits>
+  typename std::enable_if<T::kUseStaticRecordCount, uint32_t>::type
+  record_count_internal() const {
+    return Traits::kStaticRecordCount;
+  }
+
+  template <typename T = Traits>
+  typename std::enable_if<!T::kUseStaticRecordCount, uint32_t>::type
+  record_count_internal() const {
+    return data_.record_count;
+  }
+
+  template <typename T = Traits>
+  typename std::enable_if<T::kUseStaticRecordCount, void>::type
+  set_record_count(uint32_t /*record_count*/) const {}
+
+  template <typename T = Traits>
+  typename std::enable_if<!T::kUseStaticRecordCount, void>::type
+  set_record_count(uint32_t record_count) {
+    data_.record_count = record_count;
+  }
+
+  // Data we need to store for statically sized rings.
+  struct DataStaticSize {
+    Mmap* mmap = nullptr;
+  };
+
+  // Data we need to store for dynamically sized rings.
+  struct DataDynamicSize {
+    Mmap* mmap = nullptr;
+
+    // These are cached to make sure misbehaving writers cannot cause
+    // out-of-bounds memory accesses by updating the values in the mmap header.
+    uint32_t record_size = 0;
+    uint32_t record_count = 0;
+  };
+
+  using DataStaticOrDynamic =
+      typename std::conditional<Traits::kIsStaticSize, DataStaticSize,
+                                DataDynamicSize>::type;
+
+  DataStaticOrDynamic data_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BROADCAST_RING_H_
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
new file mode 100644
index 0000000..452bad0
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "buffer_hub_client.cpp",
+    "buffer_hub_rpc.cpp",
+    "ion_buffer.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+staticLibraries = [
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "libhardware",
+    "liblog",
+    "libui",
+    "libutils",
+]
+
+cc_library {
+    srcs: sourceFiles,
+    cflags: [
+        "-DLOG_TAG=\"libbufferhub\"",
+        "-DTRACE=0"
+    ],
+    export_include_dirs: localIncludeFiles,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "libbufferhub",
+}
+
+cc_test {
+    tags: ["optional"],
+    srcs: ["bufferhub_tests.cpp"],
+    static_libs: ["libbufferhub"] + staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "bufferhub_tests",
+}
+
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000..b9a53b0
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,412 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+  ALOGE_IF(!status,
+           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+  ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+
+  Status<NativeBufferHandle<LocalHandle>> status =
+      InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
+  if (!status) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  } else if (status.get().id() < 0) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!");
+    return -EIO;
+  }
+
+  auto buffer_handle = status.take();
+
+  // Stash the buffer id to replace the value in id_.
+  const int new_id = buffer_handle.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());
+
+  const int ret = buffer_handle.Import(&ion_buffer);
+  if (ret < 0)
+    return ret;
+
+  // If the import succeeds, replace the previous buffer and id.
+  buffer_ = std::move(ion_buffer);
+  id_ = new_id;
+  return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+  ATRACE_NAME("BufferHubBuffer::Poll");
+  pollfd p = {event_fd(), POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+                          void** address) {
+  return buffer_.Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock() { return buffer_.Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  int ret = Lock(usage(), 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+  return GetBlobReadWritePointer(size, addr);
+}
+
+void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count,
+                                 size_t max_fds_count) const {
+  size_t numFds = static_cast<size_t>(native_handle()->numFds);
+  *fds_count = std::min(max_fds_count, numFds);
+  std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+          strerror(-ret));
+    Close(ret);
+  }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    LocalChannelHandle channel) {
+  ATRACE_NAME("BufferConsumer::Import");
+  ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+  return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+  return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+                            size_t meta_size_bytes) {
+  ATRACE_NAME("BufferConsumer::Acquire");
+  LocalFence fence;
+  auto return_value =
+      std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+  auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+      &return_value, meta_size_bytes);
+  if (status && ready_fence)
+    *ready_fence = fence.take();
+  return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+  ATRACE_NAME("BufferConsumer::Release");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+      BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+  ATRACE_NAME("BufferConsumer::ReleaseAsync");
+  return ReturnStatusOrError(
+      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                               uint32_t usage, size_t metadata_size)
+    : BufferProducer(width, height, format, usage, usage, 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)
+    : 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",
+           event_fd(), width, height, format, producer_usage, consumer_usage,
+           metadata_size);
+
+  // (b/37881101) Deprecate producer/consumer usage
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, (producer_usage | consumer_usage), metadata_size);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+        status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, uint32_t width, uint32_t height,
+                               uint32_t format, uint32_t usage,
+                               size_t meta_size_bytes)
+    : BufferProducer(name, user_id, group_id, width, height, format, usage,
+                     usage, meta_size_bytes) {}
+
+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)
+    : 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",
+           event_fd(), name.c_str(), user_id, group_id, width, height, format,
+           producer_usage, consumer_usage, meta_size_bytes);
+
+  // (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);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create/get persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(uint32_t usage, size_t size)
+    : BufferProducer(usage, usage, size) {}
+
+BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage,
+                               size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: producer_usage=%" PRIx64
+           " consumer_usage=%" PRIx64 " size=%zu",
+           producer_usage, consumer_usage, size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+
+  // (b/37881101) Deprecate producer/consumer usage
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, (producer_usage | consumer_usage),
+      meta_size_bytes);
+  if (!status) {
+    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, uint32_t usage, size_t size)
+    : BufferProducer(name, user_id, group_id, usage, usage, size) {}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, uint64_t producer_usage,
+                               uint64_t consumer_usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+           "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " size=%zu",
+           name.c_str(), user_id, group_id, producer_usage, consumer_usage,
+           size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+
+  // (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);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+        "\"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+                         size_t meta_size_bytes) {
+  ATRACE_NAME("BufferProducer::Post");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+  ATRACE_NAME("BufferProducer::Gain");
+  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+  if (!status)
+    return -status.error();
+  if (release_fence)
+    *release_fence = status.take().take();
+  return 0;
+}
+
+int BufferProducer::GainAsync() {
+  ATRACE_NAME("BufferProducer::GainAsync");
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    LocalChannelHandle channel) {
+  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+  return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+                                   int group_id) {
+  ATRACE_NAME("BufferProducer::MakePersistent");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+                                                               group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+  ATRACE_NAME("BufferProducer::RemovePersistence");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..fa61c4a
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,226 @@
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+#define RETRY_EINTR(fnc_call)                 \
+  ([&]() -> decltype(fnc_call) {              \
+    decltype(fnc_call) result;                \
+    do {                                      \
+      result = (fnc_call);                    \
+    } while (result == -1 && errno == EINTR); \
+    return result;                            \
+  })()
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  // Check that consumers can spawn other consumers.
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(c->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+
+  EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+  // Both consumers should be triggered.
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
+
+  uint64_t context;
+  LocalHandle fence;
+  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(0)));
+
+  EXPECT_LE(0, c2->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, RETRY_EINTR(c2->Poll(0)));
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, c2->Discard());
+
+  EXPECT_LE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, p->Gain(&fence));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_LE(0, RETRY_EINTR(c->Poll(10)));
+
+  LocalHandle fence;
+  Metadata m2 = {};
+  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(m.field1, m2.field1);
+  EXPECT_EQ(m.field2, m2.field2);
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+  LocalHandle fence;
+  int64_t sequence;
+  EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  LocalHandle fence;
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Record the original buffer id for later comparison.
+  const int buffer_id = p->id();
+
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+  // Close the connection to the producer. This should not affect the consumer.
+  p = nullptr;
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+
+  // Attempt to reconnect to the persistent buffer.
+  p = BufferProducer::Create("TestPersistentBuffer");
+  ASSERT_NE(nullptr, p);
+  EXPECT_EQ(buffer_id, p->id());
+  EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Close the connection to the producer.
+  p = nullptr;
+
+  // Mismatch the params.
+  p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+                             kHeight, kFormat, kUsage);
+  ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  LocalHandle fence;
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+  EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+  // Test that removing persistence and closing the producer orphans the
+  // consumer.
+  EXPECT_EQ(0, p->RemovePersistence());
+  p = nullptr;
+
+  EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..be20e72
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  // Create a new consumer channel that is attached to the producer. Returns
+  // a file descriptor for the new channel or a negative error code.
+  Status<LocalChannelHandle> CreateConsumer();
+
+  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+  int Poll(int timeout_ms);
+
+  // Locks the area specified by (x, y, width, height) for a specific usage. If
+  // the usage is software then |addr| will be updated to point to the address
+  // of the buffer in virtual memory. The caller should only access/modify the
+  // pixels in the specified area. anything else is undefined behavior.
+  int Lock(int usage, int x, int y, int width, int height, void** addr);
+
+  // Must be called after Lock() when the caller has finished changing the
+  // buffer.
+  int Unlock();
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadWritePointer(size_t size, void** addr);
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+  // Returns a dup'd file descriptor for accessing the blob shared memory. The
+  // caller takes ownership of the file descriptor and must close it or pass on
+  // ownership. Some GPU API extensions can take file descriptors to bind shared
+  // memory gralloc buffers to GPU buffer objects.
+  LocalHandle GetBlobFd() const {
+    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+    // vendors and this is the wrong fd, late-latching and EDS will very clearly
+    // stop working and we will need to correct this. The alternative is to use
+    // a GL context in the pose service to allocate this buffer or to use the
+    // ION API directly instead of gralloc.
+    return LocalHandle(dup(native_handle()->data[0]));
+  }
+
+  // Get up to |max_fds_count| file descriptors for accessing the blob shared
+  // memory. |fds_count| will contain the actual number of file descriptors.
+  void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
+
+  using Client::event_fd;
+
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
+  native_handle_t* native_handle() const {
+    return const_cast<native_handle_t*>(buffer_.handle());
+  }
+
+  IonBuffer* buffer() { return &buffer_; }
+  const IonBuffer* buffer() const { return &buffer_; }
+
+  int id() const { return id_; }
+
+  // The following methods return settings of the first buffer. Currently,
+  // it is only possible to create multi-buffer BufferHubBuffers with the same
+  // settings.
+  uint32_t width() const { return buffer_.width(); }
+  uint32_t height() const { return buffer_.height(); }
+  uint32_t stride() const { return buffer_.stride(); }
+  uint32_t format() const { return buffer_.format(); }
+  uint32_t usage() const { return buffer_.usage(); }
+  uint32_t layer_count() const { return buffer_.layer_count(); }
+
+  // TODO(b/37881101) Clean up producer/consumer usage.
+  uint64_t producer_usage() const { return buffer_.usage(); }
+  uint64_t consumer_usage() const { return buffer_.usage(); }
+
+ protected:
+  explicit BufferHubBuffer(LocalChannelHandle channel);
+  explicit BufferHubBuffer(const std::string& endpoint_path);
+  virtual ~BufferHubBuffer();
+
+  // Initialization helper.
+  int ImportBuffer();
+
+ private:
+  BufferHubBuffer(const BufferHubBuffer&) = delete;
+  void operator=(const BufferHubBuffer&) = delete;
+
+  // Global id for the buffer that is consistent across processes. It is meant
+  // for logging and debugging purposes only and should not be used for lookup
+  // or any other functional purpose as a security precaution.
+  int id_;
+
+  IonBuffer buffer_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+  // Imports a bufferhub producer channel, assuming ownership of its handle.
+  static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferProducer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+  // |meta| are passed unaltered to the consumers. The producer must not modify
+  // the buffer until it is re-gained.
+  // This returns zero or a negative unix error code.
+  int Post(const LocalHandle& ready_fence, const void* meta,
+           size_t meta_size_bytes);
+
+  template <typename Meta,
+            typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence) {
+    return Post(ready_fence, nullptr, 0);
+  }
+  template <typename Meta, typename = typename std::enable_if<
+                               !std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence, const Meta& meta) {
+    return Post(ready_fence, &meta, sizeof(meta));
+  }
+
+  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+  // must be waited on before using the buffer. If it is not valid then the
+  // buffer is free for immediate use. This call will only succeed if the buffer
+  // is in the released state.
+  // This returns zero or a negative unix error code.
+  int Gain(LocalHandle* release_fence);
+
+  // Asynchronously marks a released buffer as gained. This method is similar to
+  // the synchronous version above, except that it does not wait for BufferHub
+  // to acknowledge success or failure, nor does it transfer a release fence to
+  // the client. This version may be used in situations where a release fence is
+  // not needed. Because of the asynchronous nature of the underlying message,
+  // no error is returned if this method is called when the buffer is in an
+  // incorrect state. Returns zero if sending the message succeeded, or a
+  // negative errno code otherwise.
+  int GainAsync();
+
+  // Attaches the producer to |name| so that it becomes a persistent buffer that
+  // may be retrieved by name at a later time. This may be used in cases where a
+  // shared memory buffer should persist across the life of the producer process
+  // (i.e. the buffer may be held by clients across a service restart). The
+  // buffer may be associated with a user and/or group id to restrict access to
+  // the buffer. If user_id or group_id is -1 then checks for the respective id
+  // are disabled. If user_id or group_id is 0 then the respective id of the
+  // calling process is used instead.
+  int MakePersistent(const std::string& name, int user_id, int group_id);
+
+  // Removes the persistence of the producer.
+  int RemovePersistence();
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through BufferProducer::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+
+  // Constructs a buffer with the given geometry and parameters.
+  BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                 uint32_t usage, size_t metadata_size = 0);
+  BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                 uint64_t producer_usage, uint64_t consumer_usage,
+                 size_t metadata_size);
+
+  // Constructs a persistent buffer with the given geometry and parameters and
+  // binds it to |name| in one shot. If a persistent buffer with the same name
+  // and settings already exists and matches the given geometry and parameters,
+  // that buffer is connected to this client instead of creating a new buffer.
+  // If the name matches but the geometry or settings do not match then
+  // construction fails and BufferProducer::Create() returns nullptr.
+  //
+  // Access to the persistent buffer may be restricted by |user_id| and/or
+  // |group_id|; these settings are established only when the buffer is first
+  // created and cannot be changed. A user or group id of -1 disables checks for
+  // that respective id. A user or group id of 0 is substituted with the
+  // effective user or group id of the calling process.
+  BufferProducer(const std::string& name, int user_id, int group_id,
+                 uint32_t width, uint32_t height, uint32_t format,
+                 uint32_t usage, size_t metadata_size = 0);
+  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);
+
+  // Constructs a blob (flat) buffer with the given usage flags.
+  BufferProducer(uint32_t usage, size_t size);
+  BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, size_t size);
+
+  // Constructs a persistent blob (flat) buffer and binds it to |name|.
+  BufferProducer(const std::string& name, int user_id, int group_id,
+                 uint32_t usage, size_t size);
+  BufferProducer(const std::string& name, int user_id, int group_id,
+                 uint64_t producer_usage, uint64_t consumer_usage, size_t size);
+
+  // Constructs a channel to persistent buffer by name only. The buffer must
+  // have been previously created or made persistent.
+  explicit BufferProducer(const std::string& name);
+
+  // Imports the given file handle to a producer channel, taking ownership.
+  explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+  // This call assumes ownership of |fd|.
+  static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferConsumer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+  // This call will only succeed after the fd is signalled. This call may be
+  // performed as an alternative to the Acquire() with metadata. In such cases
+  // the metadata is not read.
+  //
+  // This returns zero or negative unix error code.
+  int Acquire(LocalHandle* ready_fence);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence signaling that the contents of the buffer
+  // are available. This call will only succeed if the buffer is in the posted
+  // state.
+  // Returns zero on success, or a negative errno code otherwise.
+  int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+  // call will only succeed after the fd is signaled. This returns zero or a
+  // negative unix error code.
+  template <typename Meta>
+  int Acquire(LocalHandle* ready_fence, Meta* meta) {
+    return Acquire(ready_fence, meta, sizeof(*meta));
+  }
+
+  // This should be called after a successful Acquire call. If the fence is
+  // valid the fence determines the buffer usage, otherwise the buffer is
+  // released immediately.
+  // This returns zero or a negative unix error code.
+  int Release(const LocalHandle& release_fence);
+
+  // Asynchronously releases a buffer. Similar to the synchronous version above,
+  // except that it does not wait for BufferHub to reply with success or error,
+  // nor does it transfer a release fence. This version may be used in
+  // situations where a release fence is not needed. Because of the asynchronous
+  // nature of the underlying message, no error is returned if this method is
+  // called when the buffer is in an incorrect state. Returns zero if sending
+  // the message succeeded, or a negative errno code otherwise.
+  int ReleaseAsync();
+
+  // May be called after or instead of Acquire to indicate that the consumer
+  // does not need to access the buffer this cycle. This returns zero or a
+  // negative unix error code.
+  int Discard();
+
+  // When set, this consumer is no longer notified when this buffer is
+  // available. The system behaves as if Discard() is immediately called
+  // whenever the buffer is posted. If ignore is set to true while a buffer is
+  // pending, it will act as if Discard() was also called.
+  // This returns zero or a negative unix error code.
+  int SetIgnore(bool ignore);
+
+ private:
+  friend BASE;
+
+  explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000..ffdc9e2
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,241 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+  NativeBufferHandle() { Clear(); }
+  NativeBufferHandle(const IonBuffer& buffer, int id)
+      : id_(id),
+        stride_(buffer.stride()),
+        width_(buffer.width()),
+        height_(buffer.height()),
+        layer_count_(buffer.layer_count()),
+        format_(buffer.format()),
+        usage_(buffer.usage()) {
+    // Populate the fd and int vectors: native_handle->data[] is an array of fds
+    // followed by an array of opaque ints.
+    const int fd_count = buffer.handle()->numFds;
+    const int int_count = buffer.handle()->numInts;
+    for (int i = 0; i < fd_count; i++) {
+      fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+    }
+    for (int i = 0; i < int_count; i++) {
+      opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+    }
+  }
+  NativeBufferHandle(NativeBufferHandle&& other) = default;
+  NativeBufferHandle& operator=(NativeBufferHandle&& other) = default;
+
+  // Imports the native handle into the given IonBuffer instance.
+  int Import(IonBuffer* buffer) {
+    // This is annoying, but we need to convert the vector of FileHandles into a
+    // vector of ints for the Import API.
+    std::vector<int> fd_ints;
+    for (const auto& fd : fds_)
+      fd_ints.push_back(fd.Get());
+
+    const int ret =
+        buffer->Import(fd_ints.data(), fd_ints.size(), opaque_ints_.data(),
+                       opaque_ints_.size(), width_, height_, layer_count_,
+                       stride_, format_, usage_);
+    if (ret < 0)
+      return ret;
+
+    // Import succeeded, release the file handles which are now owned by the
+    // IonBuffer and clear members.
+    for (auto& fd : fds_)
+      fd.Release();
+    opaque_ints_.clear();
+    Clear();
+
+    return 0;
+  }
+
+  int id() const { return id_; }
+  size_t IntCount() const { return opaque_ints_.size(); }
+  size_t FdCount() const { return fds_.size(); }
+
+ private:
+  int id_;
+  uint32_t stride_;
+  uint32_t width_;
+  uint32_t height_;
+  uint32_t layer_count_;
+  uint32_t format_;
+  uint64_t usage_;
+  std::vector<int> opaque_ints_;
+  std::vector<FileHandleType> fds_;
+
+  void Clear() {
+    id_ = -1;
+    stride_ = width_ = height_ = format_ = usage_ = 0;
+  }
+
+  PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+                           width_, height_, layer_count_, format_, usage_,
+                           opaque_ints_, fds_);
+
+  NativeBufferHandle(const NativeBufferHandle&) = delete;
+  void operator=(const NativeBufferHandle&) = delete;
+};
+
+using BorrowedNativeBufferHandle = NativeBufferHandle<pdx::BorrowedHandle>;
+using LocalNativeBufferHandle = NativeBufferHandle<pdx::LocalHandle>;
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+  FenceHandle() = default;
+  explicit FenceHandle(int fence) : fence_{fence} {}
+  explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+  FenceHandle(FenceHandle&&) = default;
+  FenceHandle& operator=(FenceHandle&&) = default;
+
+  explicit operator bool() const { return fence_.IsValid(); }
+
+  const FileHandleType& get() const { fence_; }
+  FileHandleType&& take() { return std::move(fence_); }
+
+  int get_fd() const { return fence_.Get(); }
+  void close() { fence_.Close(); }
+
+  FenceHandle<pdx::BorrowedHandle> borrow() const {
+    return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+  }
+
+ private:
+  FileHandleType fence_;
+
+  PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+  FenceHandle(const FenceHandle&) = delete;
+  void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+struct QueueInfo {
+  size_t meta_size_bytes;
+  int id;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(QueueInfo, meta_size_bytes, id);
+};
+
+struct UsagePolicy {
+  uint64_t usage_set_mask;
+  uint64_t usage_clear_mask;
+  uint64_t usage_deny_set_mask;
+  uint64_t usage_deny_clear_mask;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(UsagePolicy, usage_set_mask, usage_clear_mask,
+                           usage_deny_set_mask, usage_deny_clear_mask);
+};
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+  // |BufferHubQueue| will keep track of at most this value of buffers.
+  // Attempts at runtime to increase the number of buffers past this
+  // will fail. Note that the value is in sync with |android::BufferQueue|, so
+  // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+  // and |android::BufferQueueProducer| which both implements the same
+  // interface: |android::IGraphicBufferProducer|.
+  static constexpr size_t kMaxQueueCapacity =
+      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+  // Op codes.
+  enum {
+    kOpCreateBuffer = 0,
+    kOpCreatePersistentBuffer,
+    kOpGetPersistentBuffer,
+    kOpGetBuffer,
+    kOpNewConsumer,
+    kOpProducerMakePersistent,
+    kOpProducerRemovePersistence,
+    kOpProducerPost,
+    kOpProducerGain,
+    kOpConsumerAcquire,
+    kOpConsumerRelease,
+    kOpConsumerSetIgnore,
+    kOpCreateProducerQueue,
+    kOpCreateConsumerQueue,
+    kOpGetQueueInfo,
+    kOpProducerQueueAllocateBuffers,
+    kOpProducerQueueDetachBuffer,
+    kOpConsumerQueueImportBuffers,
+  };
+
+  // Aliases.
+  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using LocalHandle = pdx::LocalHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+                    void(uint32_t width, uint32_t height, uint32_t format,
+                         uint64_t usage, size_t meta_size_bytes));
+  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));
+  PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+                    void(const std::string& name));
+  PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+                    NativeBufferHandle<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));
+  PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+  PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+                    std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+  PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+                    void(LocalFence release_fence));
+  PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
+
+  // Buffer Queue Methods.
+  PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+                    QueueInfo(size_t meta_size_bytes,
+                              const UsagePolicy& usage_policy));
+  PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+                    LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void));
+  PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+                    kOpProducerQueueAllocateBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(
+                        uint32_t width, uint32_t height, uint32_t layer_count,
+                        uint32_t format, uint64_t usage, size_t buffer_count));
+  PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+                    void(size_t slot));
+  PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..0d337f7
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,96 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+#include <log/log.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer();
+  IonBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t stride, uint32_t format, uint64_t usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t layer_count, uint32_t stride, uint32_t format,
+            uint64_t usage);
+  ~IonBuffer();
+
+  IonBuffer(IonBuffer&& other);
+  IonBuffer& operator=(IonBuffer&& other);
+
+  // Frees the underlying native handle and leaves the instance initialized to
+  // empty.
+  void FreeHandle();
+
+  // Allocates a new native handle with the given parameters, freeing the
+  // previous native handle if necessary. Returns 0 on success or a negative
+  // errno code otherwise. If allocation fails the previous native handle is
+  // left intact.
+  int Alloc(uint32_t width, uint32_t height, uint32_t layer_count,
+            uint32_t format, uint64_t usage);
+
+  // Resets the underlying native handle and parameters, freeing the previous
+  // native handle if necessary.
+  void Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+             uint32_t layer_count, uint32_t stride, uint32_t format,
+             uint64_t usage);
+
+  // Like Reset but also registers the native handle, which is necessary for
+  // native handles received over IPC. Returns 0 on success or a negative errno
+  // code otherwise. If import fails the previous native handle is left intact.
+  int Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+             uint32_t layer_count, uint32_t stride, uint32_t format,
+             uint64_t usage);
+
+  // Like Reset but imports a native handle from raw fd and int arrays. Returns
+  // 0 on success or a negative errno code otherwise. If import fails the
+  // previous native handle is left intact.
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, uint32_t width, uint32_t height,
+             uint32_t layer_count, uint32_t stride, uint32_t format,
+             uint64_t usage);
+
+  // Duplicates the native handle underlying |other| and then imports it. This
+  // is useful for creating multiple, independent views of the same Ion/Gralloc
+  // buffer. Returns 0 on success or a negative errno code otherwise. If
+  // duplication or import fail the previous native handle is left intact.
+  int Duplicate(const IonBuffer* other);
+
+  int Lock(uint32_t usage, int x, int y, int width, int height, void** address);
+  int LockYUV(uint32_t usage, int x, int y, int width, int height,
+              struct android_ycbcr* yuv);
+  int Unlock();
+
+  const sp<GraphicBuffer>& buffer() const { return buffer_; }
+  buffer_handle_t handle() const {
+    return buffer_.get() ? buffer_->handle : nullptr;
+  }
+  uint32_t width() const { return buffer_.get() ? buffer_->getWidth() : 0; }
+  uint32_t height() const { return buffer_.get() ? buffer_->getHeight() : 0; }
+  uint32_t layer_count() const {
+    return buffer_.get() ? buffer_->getLayerCount() : 0;
+  }
+  uint32_t stride() const { return buffer_.get() ? buffer_->getStride() : 0; }
+  uint32_t format() const {
+    return buffer_.get() ? buffer_->getPixelFormat() : 0;
+  }
+  uint64_t usage() const {
+    return buffer_.get() ? static_cast<uint64_t>(buffer_->getUsage()) : 0;
+  }
+
+ private:
+  sp<GraphicBuffer> buffer_;
+
+  IonBuffer(const IonBuffer&) = delete;
+  void operator=(const IonBuffer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000..b4ef2f5
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,176 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+    : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+                                        android::LightRefBase<NativeBuffer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+      : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    handle = buffer_->handle();
+  }
+
+  virtual ~NativeBuffer() {}
+
+  std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+  int fence() const { return fence_.Get(); }
+
+  void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+  friend class android::LightRefBase<NativeBuffer>;
+
+  std::shared_ptr<IonBuffer> buffer_;
+  pdx::LocalHandle fence_;
+
+  NativeBuffer(const NativeBuffer&) = delete;
+  void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferProducer,
+                                 android::LightRefBase<NativeBufferProducer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+                       EGLDisplay display, uint32_t surface_buffer_index)
+      : BASE(),
+        buffer_(buffer),
+        surface_buffer_index_(surface_buffer_index),
+        display_(display) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+  }
+
+  explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+      : NativeBufferProducer(buffer, nullptr, 0) {}
+
+  virtual ~NativeBufferProducer() {
+    for (EGLImageKHR egl_image : egl_images_) {
+      if (egl_image != EGL_NO_IMAGE_KHR)
+        eglDestroyImageKHR(display_, egl_image);
+    }
+  }
+
+  EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+  std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+  int release_fence() const { return release_fence_.Get(); }
+  uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+  // Return the release fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+  // Post the buffer consumer, closing the acquire and release fences.
+  int Post(int acquire_fence, uint64_t sequence) {
+    release_fence_.Close();
+    return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+  }
+
+  // Gain the buffer producer, closing the previous release fence if valid.
+  int Gain() { return buffer_->Gain(&release_fence_); }
+
+  // Asynchronously gain the buffer, closing the previous release fence.
+  int GainAsync() {
+    release_fence_.Close();
+    return buffer_->GainAsync();
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducer>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+  pdx::LocalHandle release_fence_;
+  std::vector<EGLImageKHR> egl_images_;
+  uint32_t surface_buffer_index_;
+  EGLDisplay display_;
+
+  NativeBufferProducer(const NativeBufferProducer&) = delete;
+  void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferConsumer,
+                                 android::LightRefBase<NativeBufferConsumer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+      : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+  }
+
+  virtual ~NativeBufferConsumer() {}
+
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+  uint64_t sequence() const { return sequence_; }
+
+  // Return the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+  // Acquire the underlying buffer consumer, closing the previous acquire fence
+  // if valid.
+  int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+  // Release the buffer consumer, closing the acquire and release fences if
+  // valid.
+  int Release(int release_fence) {
+    acquire_fence_.Close();
+    sequence_ = 0;
+    return buffer_->Release(pdx::LocalHandle(release_fence));
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferConsumer>;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  pdx::LocalHandle acquire_fence_;
+  uint64_t sequence_;
+
+  NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+  void operator=(NativeBufferConsumer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..cbaa24a
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,240 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <log/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace {
+
+constexpr uint32_t kDefaultGraphicBufferLayerCount = 1;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format,
+                     uint64_t usage)
+    : IonBuffer() {
+  Alloc(width, height, kDefaultGraphicBufferLayerCount, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+                     uint32_t stride, uint32_t format, uint64_t usage)
+    : IonBuffer(handle, width, height, kDefaultGraphicBufferLayerCount, stride,
+                format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+                     uint32_t layer_count, uint32_t stride, uint32_t format,
+                     uint64_t usage)
+    : buffer_(nullptr) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::IonBuffer: handle=%p width=%u height=%u layer_count=%u "
+           "stride=%u format=%u usage=%" PRIx64,
+           handle, width, height, layer_count, stride, format, usage);
+  if (handle != 0) {
+    Import(handle, width, height, layer_count, stride, format, usage);
+  }
+}
+
+IonBuffer::~IonBuffer() {
+  ALOGD_IF(TRACE,
+           "IonBuffer::~IonBuffer: handle=%p width=%u height=%u stride=%u "
+           "format=%u usage=%" PRIx64,
+           handle(), width(), height(), stride(), format(), usage());
+  FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+  *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+  ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle(),
+           other.handle());
+
+  if (this != &other) {
+    buffer_ = other.buffer_;
+    other.FreeHandle();
+  }
+  return *this;
+}
+
+void IonBuffer::FreeHandle() {
+  if (buffer_.get()) {
+    // GraphicBuffer unregisters and cleans up the handle if needed
+    buffer_ = nullptr;
+  }
+}
+
+int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t layer_count,
+                     uint32_t format, uint64_t usage) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::Alloc: width=%u height=%u layer_count=%u format=%u "
+           "usage=%" PRIx64, width, height, layer_count, format, usage);
+
+  sp<GraphicBuffer> buffer =
+      new GraphicBuffer(width, height, format, layer_count, usage);
+  if (buffer->initCheck() != OK) {
+    ALOGE("IonBuffer::Aloc: Failed to allocate buffer");
+    return -EINVAL;
+  } else {
+    buffer_ = buffer;
+    return 0;
+  }
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t layer_count, uint32_t stride, uint32_t format,
+                      uint64_t usage) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::Reset: handle=%p width=%u height=%u layer_count=%u "
+           "stride=%u format=%u usage=%" PRIx64,
+           handle, width, height, layer_count, stride, format, usage);
+  Import(handle, width, height, layer_count, stride, format, usage);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t layer_count, uint32_t stride, uint32_t format,
+                      uint64_t usage) {
+  ATRACE_NAME("IonBuffer::Import1");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Import: handle=%p width=%u height=%u layer_count=%u "
+           "stride=%u format=%u usage=%" PRIx64,
+           handle, width, height, layer_count, stride, format, usage);
+  FreeHandle();
+  sp<GraphicBuffer> buffer =
+      new GraphicBuffer(handle, GraphicBuffer::TAKE_UNREGISTERED_HANDLE, width,
+                        height, format, layer_count, usage, stride);
+  if (buffer->initCheck() != OK) {
+    ALOGE("IonBuffer::Import: Failed to import buffer");
+    return -EINVAL;
+  } else {
+    buffer_ = buffer;
+    return 0;
+  }
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+                      int int_count, uint32_t width, uint32_t height,
+                      uint32_t layer_count, uint32_t stride, uint32_t format,
+                      uint64_t usage) {
+  ATRACE_NAME("IonBuffer::Import2");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Import: fd_count=%d int_count=%d width=%u height=%u "
+           "layer_count=%u stride=%u format=%u usage=%" PRIx64,
+           fd_count, int_count, width, height, layer_count, stride, format,
+           usage);
+
+  if (fd_count < 0 || int_count < 0) {
+    ALOGE("IonBuffer::Import: invalid arguments.");
+    return -EINVAL;
+  }
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Import: failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Copy fd_array into the first part of handle->data and int_array right
+  // after it.
+  memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+  memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+  const int ret =
+      Import(handle, width, height, layer_count, stride, format, usage);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+  if (!other->handle())
+    return -EINVAL;
+
+  const int fd_count = other->handle()->numFds;
+  const int int_count = other->handle()->numInts;
+
+  if (fd_count < 0 || int_count < 0)
+    return -EINVAL;
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Duplicate the file descriptors from the other native handle.
+  for (int i = 0; i < fd_count; i++)
+    handle->data[i] = dup(other->handle()->data[i]);
+
+  // Copy the ints after the file descriptors.
+  memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+         sizeof(int) * int_count);
+
+  const int ret =
+      Import(handle, other->width(), other->height(), other->layer_count(),
+             other->stride(), other->format(), other->usage());
+  if (ret < 0) {
+    ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height,
+                    void** address) {
+  ATRACE_NAME("IonBuffer::Lock");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+           "address=%p",
+           handle(), usage, x, y, width, height, address);
+
+  status_t err =
+      buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
+  if (err != NO_ERROR)
+    return -EINVAL;
+  else
+    return 0;
+}
+
+int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height,
+                       struct android_ycbcr* yuv) {
+  ATRACE_NAME("IonBuffer::LockYUV");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+           handle(), usage, x, y, width, height);
+
+  status_t err =
+      buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
+  if (err != NO_ERROR)
+    return -EINVAL;
+  else
+    return 0;
+}
+
+int IonBuffer::Unlock() {
+  ATRACE_NAME("IonBuffer::Unlock");
+  ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
+
+  status_t err = buffer_->unlock();
+  if (err != NO_ERROR)
+    return -EINVAL;
+  else
+    return 0;
+}
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
new file mode 100644
index 0000000..a587f95
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -0,0 +1,59 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "buffer_hub_queue_client.cpp",
+    "buffer_hub_queue_producer.cpp",
+]
+
+includeFiles = [
+    "include",
+]
+
+headerLibraries = [
+    "libdvr_headers",
+]
+
+staticLibraries = [
+    "libbufferhub",
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libbinder",
+    "libcutils",
+    "libhardware",
+    "liblog",
+    "libui",
+    "libutils",
+    "libgui",
+]
+
+cc_library {
+    name: "libbufferhubqueue",
+    cflags: [
+        "-DLOG_TAG=\"libbufferhubqueue\"",
+        "-DTRACE=0",
+    ],
+    srcs: sourceFiles,
+    export_include_dirs: includeFiles,
+    export_static_lib_headers: staticLibraries,
+    header_libs: headerLibraries,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+}
+
+subdirs = ["tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
new file mode 100644
index 0000000..012a4e7
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,635 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sys/epoll.h>
+
+#include <array>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+#define RETRY_EINTR(fnc_call)                 \
+  ([&]() -> decltype(fnc_call) {              \
+    decltype(fnc_call) result;                \
+    do {                                      \
+      result = (fnc_call);                    \
+    } while (result == -1 && errno == EINTR); \
+    return result;                            \
+  })()
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      meta_size_(0),
+      buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
+      available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+      fences_(BufferHubQueue::kMaxQueueCapacity),
+      capacity_(0),
+      id_(-1) {
+  Initialize();
+}
+
+BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      meta_size_(0),
+      buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
+      available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+      fences_(BufferHubQueue::kMaxQueueCapacity),
+      capacity_(0),
+      id_(-1) {
+  Initialize();
+}
+
+void BufferHubQueue::Initialize() {
+  int ret = epoll_fd_.Create();
+  if (ret < 0) {
+    ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
+          strerror(-ret));
+    return;
+  }
+
+  epoll_event event = {.events = EPOLLIN | EPOLLET,
+                       .data = {.u64 = static_cast<uint64_t>(
+                                    BufferHubQueue::kEpollQueueEventIndex)}};
+  ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
+  if (ret < 0) {
+    ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s",
+          strerror(-ret));
+  }
+}
+
+Status<void> BufferHubQueue::ImportQueue() {
+  auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
+  if (!status) {
+    ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s",
+          status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  } else {
+    SetupQueue(status.get().meta_size_bytes, status.get().id);
+    return {};
+  }
+}
+
+void BufferHubQueue::SetupQueue(size_t meta_size_bytes, int id) {
+  meta_size_ = meta_size_bytes;
+  id_ = id;
+  meta_buffer_tmp_.reset(meta_size_ > 0 ? new uint8_t[meta_size_] : nullptr);
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
+  if (auto status = CreateConsumerQueueHandle())
+    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));
+  else
+    return nullptr;
+}
+
+Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() {
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+  if (!status) {
+    ALOGE(
+        "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
+        "%s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  }
+
+  return status;
+}
+
+bool BufferHubQueue::WaitForBuffers(int timeout) {
+  std::array<epoll_event, kMaxEvents> events;
+
+  // Loop at least once to check for hangups.
+  do {
+    ALOGD_IF(
+        TRACE,
+        "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
+        id(), count(), capacity());
+
+    // If there is already a buffer then just check for hangup without waiting.
+    const int ret = epoll_fd_.Wait(events.data(), events.size(),
+                                   count() == 0 ? timeout : 0);
+
+    if (ret == 0) {
+      ALOGI_IF(TRACE,
+               "BufferHubQueue::WaitForBuffers: No events before timeout: "
+               "queue_id=%d",
+               id());
+      return count() != 0;
+    }
+
+    if (ret < 0 && ret != -EINTR) {
+      ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s",
+            strerror(-ret));
+      return false;
+    }
+
+    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
+    // client itself.
+    for (int i = 0; i < num_events; i++) {
+      int64_t index = static_cast<int64_t>(events[i].data.u64);
+
+      ALOGD_IF(TRACE,
+               "BufferHubQueue::WaitForBuffers: event %d: index=%" PRId64, i,
+               index);
+
+      if (is_buffer_event_index(index)) {
+        HandleBufferEvent(static_cast<size_t>(index), events[i].events);
+      } else if (is_queue_event_index(index)) {
+        HandleQueueEvent(events[i].events);
+      } else {
+        ALOGW("BufferHubQueue::WaitForBuffers: Unknown event index: %" PRId64,
+              index);
+      }
+    }
+  } while (count() == 0 && capacity() > 0 && !hung_up());
+
+  return count() != 0;
+}
+
+void BufferHubQueue::HandleBufferEvent(size_t slot, int poll_events) {
+  auto buffer = buffers_[slot];
+  if (!buffer) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
+    return;
+  }
+
+  auto status = buffer->GetEventMask(poll_events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  const int events = status.get();
+  if (events & EPOLLIN) {
+    const int ret = OnBufferReady(buffer, &fences_[slot]);
+    if (ret == 0 || ret == -EALREADY || ret == -EBUSY) {
+      // Only enqueue the buffer if it moves to or is already in the state
+      // requested in OnBufferReady(). 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.
+      if (ret != -EBUSY)
+        Enqueue(buffer, slot);
+    } else {
+      ALOGE(
+          "BufferHubQueue::HandleBufferEvent: Failed to set buffer ready, "
+          "queue_id=%d buffer_id=%d: %s",
+          id(), buffer->id(), strerror(-ret));
+    }
+  } else if (events & EPOLLHUP) {
+    // This might be caused by producer replacing an existing buffer slot, or
+    // when BufferHubQueue is shutting down. For the first case, currently the
+    // epoll FD is cleaned up when the replacement consumer client is imported,
+    // we shouldn't detach again if |epollhub_pending_[slot]| is set.
+    ALOGW(
+        "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP at slot: %zu, "
+        "buffer event fd: %d, EPOLLHUP pending: %d",
+        slot, buffer->event_fd(), int{epollhup_pending_[slot]});
+    if (epollhup_pending_[slot]) {
+      epollhup_pending_[slot] = false;
+    } else {
+      DetachBuffer(slot);
+    }
+  } else {
+    ALOGW(
+        "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
+        "events=%d",
+        slot, events);
+  }
+}
+
+void BufferHubQueue::HandleQueueEvent(int poll_event) {
+  auto status = GetEventMask(poll_event);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  const int events = status.get();
+  if (events & EPOLLIN) {
+    // Note that after buffer imports, if |count()| still returns 0, epoll
+    // wait will be tried again to acquire the newly imported buffer.
+    auto buffer_status = OnBufferAllocated();
+    if (!buffer_status) {
+      ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s",
+            buffer_status.GetErrorMessage().c_str());
+    }
+  } else if (events & EPOLLHUP) {
+    ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!");
+    hung_up_ = true;
+  } else {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events);
+  }
+}
+
+int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf,
+                              size_t slot) {
+  if (is_full()) {
+    // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's
+    // import buffer.
+    ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
+          capacity_);
+    return -E2BIG;
+  }
+
+  if (buffers_[slot] != nullptr) {
+    // Replace the buffer if the slot is preoccupied. This could happen when the
+    // producer side replaced the slot with a newly allocated buffer. Detach the
+    // buffer before setting up with the new one.
+    DetachBuffer(slot);
+    epollhup_pending_[slot] = true;
+  }
+
+  epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
+  const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event);
+  if (ret < 0) {
+    ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+          strerror(-ret));
+    return ret;
+  }
+
+  buffers_[slot] = buf;
+  capacity_++;
+  return 0;
+}
+
+int BufferHubQueue::DetachBuffer(size_t slot) {
+  auto& buf = buffers_[slot];
+  if (buf == nullptr) {
+    ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot);
+    return -EINVAL;
+  }
+
+  const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
+  if (ret < 0) {
+    ALOGE(
+        "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: "
+        "%s",
+        strerror(-ret));
+    return ret;
+  }
+
+  buffers_[slot] = nullptr;
+  capacity_--;
+  return 0;
+}
+
+void BufferHubQueue::Enqueue(const std::shared_ptr<BufferHubBuffer>& buf,
+                             size_t slot) {
+  if (count() == capacity_) {
+    ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!");
+    return;
+  }
+
+  // Set slot buffer back to vector.
+  // TODO(jwcai) Here have to dynamically allocate BufferInfo::metadata due to
+  // the limitation of the RingBuffer we are using. Would be better to refactor
+  // that.
+  BufferInfo buffer_info(slot, meta_size_);
+  buffer_info.buffer = buf;
+  // Swap metadata loaded during onBufferReady into vector.
+  std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+  available_buffers_.Append(std::move(buffer_info));
+}
+
+Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(
+    int timeout, size_t* slot, void* meta, LocalHandle* fence) {
+  ALOGD_IF(TRACE, "Dequeue: count=%zu, timeout=%d", count(), timeout);
+
+  if (!WaitForBuffers(timeout))
+    return ErrorStatus(ETIMEDOUT);
+
+  std::shared_ptr<BufferHubBuffer> buf;
+  BufferInfo& buffer_info = available_buffers_.Front();
+
+  *fence = std::move(fences_[buffer_info.slot]);
+
+  // Report current pos as the output slot.
+  std::swap(buffer_info.slot, *slot);
+  // Swap buffer from vector to be returned later.
+  std::swap(buffer_info.buffer, buf);
+  // Swap metadata from vector into tmp so that we can write out to |meta|.
+  std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+  available_buffers_.PopFront();
+
+  if (!buf) {
+    ALOGE("BufferHubQueue::Dequeue: Buffer to be dequeued is nullptr");
+    return ErrorStatus(ENOBUFS);
+  }
+
+  if (meta) {
+    std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_,
+              reinterpret_cast<uint8_t*>(meta));
+  }
+
+  return {std::move(buf)};
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size)
+    : ProducerQueue(meta_size, 0, 0, 0, 0) {}
+
+ProducerQueue::ProducerQueue(LocalChannelHandle handle)
+    : BASE(std::move(handle)) {
+  auto status = ImportQueue();
+  if (!status) {
+    ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+  }
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size, uint64_t usage_set_mask,
+                             uint64_t usage_clear_mask,
+                             uint64_t usage_deny_set_mask,
+                             uint64_t usage_deny_clear_mask)
+    : BASE(BufferHubRPC::kClientPath) {
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+      meta_size, UsagePolicy{usage_set_mask, usage_clear_mask,
+                             usage_deny_set_mask, usage_deny_clear_mask});
+  if (!status) {
+    ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  SetupQueue(status.get().meta_size_bytes, status.get().id);
+}
+
+int ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
+                                  uint32_t layer_count, uint32_t format,
+                                  uint64_t usage, size_t* out_slot) {
+  if (out_slot == nullptr) {
+    ALOGE("ProducerQueue::AllocateBuffer: Parameter out_slot cannot be null.");
+    return -EINVAL;
+  }
+
+  if (is_full()) {
+    ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu",
+          capacity());
+    return -E2BIG;
+  }
+
+  const size_t kBufferCount = 1U;
+  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+          width, height, layer_count, format, usage, kBufferCount);
+  if (!status) {
+    ALOGE("ProducerQueue::AllocateBuffer failed to create producer buffer: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  auto buffer_handle_slots = status.take();
+  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount,
+                      "BufferHubRPC::ProducerQueueAllocateBuffers should "
+                      "return one and only one buffer handle.");
+
+  // We only allocate one buffer at a time.
+  auto& buffer_handle = buffer_handle_slots[0].first;
+  size_t buffer_slot = buffer_handle_slots[0].second;
+  ALOGD_IF(TRACE,
+           "ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d",
+           buffer_handle.value());
+
+  *out_slot = buffer_slot;
+  return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+                   buffer_slot);
+}
+
+int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf,
+                             size_t slot) {
+  ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
+           id(), buf->id(), slot);
+  // For producer buffer, we need to enqueue the newly added buffer
+  // immediately. Producer queue starts with all buffers in available state.
+  const int ret = BufferHubQueue::AddBuffer(buf, slot);
+  if (ret < 0)
+    return ret;
+
+  Enqueue(buf, slot);
+  return 0;
+}
+
+int ProducerQueue::DetachBuffer(size_t slot) {
+  auto status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
+  if (!status) {
+    ALOGE("ProducerQueue::DetachBuffer: Failed to detach producer buffer: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  return BufferHubQueue::DetachBuffer(slot);
+}
+
+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);
+    return ErrorStatus(EINVAL);
+  }
+
+  auto buffer_status =
+      BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence);
+  if (!buffer_status)
+    return buffer_status.error_status();
+
+  return {std::static_pointer_cast<BufferProducer>(buffer_status.take())};
+}
+
+int ProducerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+                                 LocalHandle* release_fence) {
+  ALOGD_IF(TRACE, "ProducerQueue::OnBufferReady: queue_id=%d buffer_id=%d",
+           id(), buf->id());
+  auto buffer = std::static_pointer_cast<BufferProducer>(buf);
+  return buffer->Gain(release_fence);
+}
+
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import)
+    : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) {
+  auto status = ImportQueue();
+  if (!status) {
+    ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+  }
+
+  auto import_status = ImportBuffers();
+  if (import_status) {
+    ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.",
+          import_status.get());
+  } else {
+    ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s",
+          import_status.GetErrorMessage().c_str());
+  }
+}
+
+Status<size_t> ConsumerQueue::ImportBuffers() {
+  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
+  if (!status) {
+    ALOGE("ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
+          status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  }
+
+  int ret;
+  int last_error = 0;
+  int imported_buffers = 0;
+
+  auto buffer_handle_slots = status.take();
+  for (auto& buffer_handle_slot : buffer_handle_slots) {
+    ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d",
+             buffer_handle_slot.first.value());
+
+    std::unique_ptr<BufferConsumer> buffer_consumer =
+        BufferConsumer::Import(std::move(buffer_handle_slot.first));
+
+    // 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 = ret;
+      }
+    }
+
+    ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+    if (ret < 0) {
+      ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s",
+            strerror(-ret));
+      last_error = ret;
+      continue;
+    } else {
+      imported_buffers++;
+    }
+  }
+
+  if (imported_buffers > 0)
+    return {imported_buffers};
+  else
+    return ErrorStatus(-last_error);
+}
+
+int ConsumerQueue::AddBuffer(const std::shared_ptr<BufferConsumer>& buf,
+                             size_t slot) {
+  ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
+           id(), buf->id(), slot);
+  const int ret = BufferHubQueue::AddBuffer(buf, slot);
+  if (ret < 0)
+    return ret;
+
+  // 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
+  // set.
+  const int kTimeoutMs = 0;
+  pollfd pfd{buf->event_fd(), POLLIN, 0};
+  const int count = RETRY_EINTR(poll(&pfd, 1, kTimeoutMs));
+  if (count < 0) {
+    const int error = errno;
+    ALOGE("ConsumerQueue::AddBuffer: Failed to poll consumer buffer: %s",
+          strerror(errno));
+    return -error;
+  }
+
+  if (count == 1)
+    HandleBufferEvent(slot, pfd.revents);
+
+  return 0;
+}
+
+Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+    int timeout, size_t* slot, void* meta, size_t meta_size,
+    LocalHandle* acquire_fence) {
+  if (meta_size != meta_size_) {
+    ALOGE(
+        "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer "
+        "does not match metadata size (%zu) for the queue.",
+        meta_size, meta_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);
+  }
+
+  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())};
+}
+
+int ConsumerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+                                 LocalHandle* acquire_fence) {
+  ALOGD_IF(TRACE, "ConsumerQueue::OnBufferReady: queue_id=%d buffer_id=%d",
+           id(), buf->id());
+  auto buffer = std::static_pointer_cast<BufferConsumer>(buf);
+  return buffer->Acquire(acquire_fence, meta_buffer_tmp_.get(), meta_size_);
+}
+
+Status<void> ConsumerQueue::OnBufferAllocated() {
+  auto status = ImportBuffers();
+  if (!status) {
+    ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s",
+          status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  } else if (status.get() == 0) {
+    ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!");
+    return ErrorStatus(ENOBUFS);
+  } else {
+    ALOGD_IF(TRACE,
+             "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.",
+             status.get());
+    return {};
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
new file mode 100644
index 0000000..8582bbf
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,643 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+#include <dvr/dvr_api.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+/* static */
+sp<BufferHubQueueProducer> BufferHubQueueProducer::Create() {
+  sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
+  producer->queue_ = ProducerQueue::Create<DvrNativeBufferMetadata>();
+  return producer;
+}
+
+/* static */
+sp<BufferHubQueueProducer> BufferHubQueueProducer::Create(
+    const std::shared_ptr<ProducerQueue>& queue) {
+  if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
+    ALOGE(
+        "BufferHubQueueProducer::Create producer's metadata size is different "
+        "than the size of DvrNativeBufferMetadata");
+    return nullptr;
+  }
+
+  sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
+  producer->queue_ = queue;
+  return producer;
+}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+                                               sp<GraphicBuffer>* buf) {
+  ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (connected_api_ == kNoConnectedApi) {
+    ALOGE("requestBuffer: BufferHubQueueProducer has no connected producer");
+    return NO_INIT;
+  }
+
+  if (slot < 0 || slot >= max_buffer_count_) {
+    ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot,
+          max_buffer_count_);
+    return BAD_VALUE;
+  } else if (!buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if (buffers_[slot].mGraphicBuffer != nullptr) {
+    ALOGE("requestBuffer: slot %d is not empty.", slot);
+    return BAD_VALUE;
+  } else if (buffers_[slot].mBufferProducer == nullptr) {
+    ALOGE("requestBuffer: slot %d is not dequeued.", slot);
+    return BAD_VALUE;
+  }
+
+  const auto& buffer_producer = buffers_[slot].mBufferProducer;
+  sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+
+  buffers_[slot].mGraphicBuffer = graphic_buffer;
+  buffers_[slot].mRequestBufferCalled = true;
+
+  *buf = graphic_buffer;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
+    int max_dequeued_buffers) {
+  ALOGD_IF(TRACE, "setMaxDequeuedBufferCount: max_dequeued_buffers=%d",
+           max_dequeued_buffers);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (max_dequeued_buffers <= 0 ||
+      max_dequeued_buffers >
+          static_cast<int>(BufferHubQueue::kMaxQueueCapacity -
+                           kDefaultUndequeuedBuffers)) {
+    ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]",
+          max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity);
+    return BAD_VALUE;
+  }
+
+  // The new dequeued_buffers count should not be violated by the number
+  // of currently dequeued buffers.
+  int dequeued_count = 0;
+  for (const auto& buf : buffers_) {
+    if (buf.mBufferState.isDequeued()) {
+      dequeued_count++;
+    }
+  }
+  if (dequeued_count > max_dequeued_buffers) {
+    ALOGE(
+        "setMaxDequeuedBufferCount: the requested dequeued_buffers"
+        "count (%d) exceeds the current dequeued buffer count (%d)",
+        max_dequeued_buffers, dequeued_count);
+    return BAD_VALUE;
+  }
+
+  max_dequeued_buffer_count_ = max_dequeued_buffers;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAsyncMode(bool async) {
+  if (async) {
+    // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
+    // automatically and behaves differently from IGraphicBufferConsumer. Thus,
+    // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
+    // to prevent dequeueBuffer from being blocking) technically does not apply
+    // here.
+    //
+    // In Daydream, non-blocking producer side dequeue is guaranteed by careful
+    // buffer consumer implementations. In another word, BufferHubQueue based
+    // dequeueBuffer should never block whether setAsyncMode(true) is set or
+    // not.
+    //
+    // See: IGraphicBufferProducer::setAsyncMode and
+    // BufferQueueProducer::setAsyncMode for more about original implementation.
+    ALOGW(
+        "BufferHubQueueProducer::setAsyncMode: BufferHubQueue should always be "
+        "asynchronous. This call makes no effact.");
+    return NO_ERROR;
+  }
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::dequeueBuffer(
+    int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
+    PixelFormat format, uint32_t usage,
+    FrameEventHistoryDelta* /* out_timestamps */) {
+  ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%u", width,
+           height, format, usage);
+
+  status_t ret;
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (connected_api_ == kNoConnectedApi) {
+    ALOGE("dequeueBuffer: BufferQueue has no connected producer");
+    return NO_INIT;
+  }
+
+  const uint32_t kLayerCount = 1;
+  if (static_cast<int32_t>(queue_->capacity()) <
+      max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
+    // Lazy allocation. When the capacity of |queue_| has not reached
+    // |max_dequeued_buffer_count_|, allocate new buffer.
+    // TODO(jwcai) To save memory, the really reasonable thing to do is to go
+    // over existing slots and find first existing one to dequeue.
+    ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+    if (ret < 0)
+      return ret;
+  }
+
+  size_t slot;
+  std::shared_ptr<BufferProducer> buffer_producer;
+
+  for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
+    LocalHandle fence;
+    auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
+
+    buffer_producer = buffer_status.take();
+    if (!buffer_producer)
+      return NO_MEMORY;
+
+    if (width == buffer_producer->width() &&
+        height == buffer_producer->height() &&
+        static_cast<uint32_t>(format) == buffer_producer->format()) {
+      // The producer queue returns a buffer producer matches the request.
+      break;
+    }
+
+    // Needs reallocation.
+    // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
+    ALOGI(
+        "dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
+        "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
+        "re-allocattion.",
+        width, height, format, slot, buffer_producer->width(),
+        buffer_producer->height(), buffer_producer->format());
+    // Mark the slot as reallocating, so that later we can set
+    // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
+    buffers_[slot].mIsReallocating = true;
+
+    // Remove the old buffer once the allocation before allocating its
+    // replacement.
+    RemoveBuffer(slot);
+
+    // Allocate a new producer buffer with new buffer configs. Note that if
+    // there are already multiple buffers in the queue, the next one returned
+    // from |queue_->Dequeue| may not be the new buffer we just reallocated.
+    // Retry up to BufferHubQueue::kMaxQueueCapacity times.
+    ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+    if (ret < 0)
+      return ret;
+  }
+
+  // With the BufferHub backed solution. Buffer slot returned from
+  // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
+  // 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);
+
+  buffers_[slot].mBufferState.freeQueued();
+  buffers_[slot].mBufferState.dequeue();
+  ALOGD_IF(TRACE, "dequeueBuffer: slot=%zu", slot);
+
+  // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
+  // just need to exopose that through |BufferHubQueue| once we need fence.
+  *out_fence = Fence::NO_FENCE;
+  *out_slot = slot;
+  ret = NO_ERROR;
+
+  if (buffers_[slot].mIsReallocating) {
+    ret |= BUFFER_NEEDS_REALLOCATION;
+    buffers_[slot].mIsReallocating = false;
+  }
+
+  return ret;
+}
+
+status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
+  ALOGE("BufferHubQueueProducer::detachBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::detachNextBuffer(
+    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
+  ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::attachBuffer(
+    int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) {
+  // With this BufferHub backed implementation, we assume (for now) all buffers
+  // are allocated and owned by the BufferHub. Thus the attempt of transfering
+  // ownership of a buffer to the buffer queue is intentionally unsupported.
+  LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::queueBuffer(int slot,
+                                             const QueueBufferInput& input,
+                                             QueueBufferOutput* output) {
+  ALOGD_IF(TRACE, "queueBuffer: slot %d", slot);
+
+  if (output == nullptr) {
+    return BAD_VALUE;
+  }
+
+  int64_t timestamp;
+  bool is_auto_timestamp;
+  android_dataspace dataspace;
+  Rect crop(Rect::EMPTY_RECT);
+  int scaling_mode;
+  uint32_t transform;
+  sp<Fence> fence;
+
+  input.deflate(&timestamp, &is_auto_timestamp, &dataspace, &crop,
+                &scaling_mode, &transform, &fence);
+
+  // Check input scaling mode is valid.
+  switch (scaling_mode) {
+    case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+    case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+    case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+    case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+      break;
+    default:
+      ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
+      return BAD_VALUE;
+  }
+
+  // Check input fence is valid.
+  if (fence == nullptr) {
+    ALOGE("queueBuffer: fence is NULL");
+    return BAD_VALUE;
+  }
+
+  status_t ret;
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (connected_api_ == kNoConnectedApi) {
+    ALOGE("queueBuffer: BufferQueue has no connected producer");
+    return NO_INIT;
+  }
+
+  if (slot < 0 || slot >= max_buffer_count_) {
+    ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot,
+          max_buffer_count_);
+    return BAD_VALUE;
+  } else if (!buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if ((!buffers_[slot].mRequestBufferCalled ||
+              buffers_[slot].mGraphicBuffer == nullptr)) {
+    ALOGE(
+        "queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
+        "mGraphicBuffer=%p)",
+        slot, buffers_[slot].mRequestBufferCalled,
+        buffers_[slot].mGraphicBuffer.get());
+    return BAD_VALUE;
+  }
+
+  // Post the buffer producer with timestamp in the metadata.
+  const auto& buffer_producer = buffers_[slot].mBufferProducer;
+
+  // Check input crop is not out of boundary of current buffer.
+  Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
+  Rect cropped_rect(Rect::EMPTY_RECT);
+  crop.intersect(buffer_rect, &cropped_rect);
+  if (cropped_rect != crop) {
+    ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
+    return BAD_VALUE;
+  }
+
+  LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
+
+  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);
+  meta_data.crop_left = crop.left;
+  meta_data.crop_top = crop.top;
+  meta_data.crop_right = crop.right;
+  meta_data.crop_bottom = crop.bottom;
+  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));
+  buffers_[slot].mBufferState.queue();
+
+  output->width = buffer_producer->width();
+  output->height = buffer_producer->height();
+  output->transformHint = 0;  // default value, we don't use it yet.
+
+  // |numPendingBuffers| counts of the number of buffers that has been enqueued
+  // by the producer but not yet acquired by the consumer. Due to the nature
+  // of BufferHubQueue design, this is hard to trace from the producer's client
+  // side, but it's safe to assume it's zero.
+  output->numPendingBuffers = 0;
+
+  // Note that we are not setting nextFrameNumber here as it seems to be only
+  // used by surface flinger. See more at b/22802885, ag/791760.
+  output->nextFrameNumber = 0;
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::cancelBuffer(int slot,
+                                              const sp<Fence>& fence) {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (connected_api_ == kNoConnectedApi) {
+    ALOGE("cancelBuffer: BufferQueue has no connected producer");
+    return NO_INIT;
+  }
+
+  if (slot < 0 || slot >= max_buffer_count_) {
+    ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+          max_buffer_count_);
+    return BAD_VALUE;
+  } else if (!buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if (fence == nullptr) {
+    ALOGE("cancelBuffer: fence is NULL");
+    return BAD_VALUE;
+  }
+
+  auto buffer_producer = buffers_[slot].mBufferProducer;
+  queue_->Enqueue(buffer_producer, slot);
+  buffers_[slot].mBufferState.cancel();
+  buffers_[slot].mFence = fence;
+  ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot);
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::query(int what, int* out_value) {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (out_value == nullptr) {
+    ALOGE("query: out_value was NULL");
+    return BAD_VALUE;
+  }
+
+  int value = 0;
+  switch (what) {
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+      // TODO(b/36187402) This should be the maximum number of buffers that this
+      // producer queue's consumer can acquire. Set to be at least one. Need to
+      // find a way to set from the consumer side.
+      value = kDefaultUndequeuedBuffers;
+      break;
+    case NATIVE_WINDOW_BUFFER_AGE:
+      value = 0;
+      break;
+    case NATIVE_WINDOW_WIDTH:
+      value = queue_->default_width();
+      break;
+    case NATIVE_WINDOW_HEIGHT:
+      value = queue_->default_height();
+      break;
+    case NATIVE_WINDOW_FORMAT:
+      value = queue_->default_format();
+      break;
+    case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+      // BufferHubQueue is always operating in async mode, thus semantically
+      // consumer can never be running behind. See BufferQueueCore.cpp core
+      // for more information about the original meaning of this flag.
+      value = 0;
+      break;
+    case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+      // TODO(jwcai) This is currently not implement as we don't need
+      // IGraphicBufferConsumer parity.
+      value = 0;
+      break;
+    case NATIVE_WINDOW_DEFAULT_DATASPACE:
+      // TODO(jwcai) Return the default value android::BufferQueue is using as
+      // there is no way dvr::ConsumerQueue can set it.
+      value = 0;  // HAL_DATASPACE_UNKNOWN
+      break;
+    case NATIVE_WINDOW_STICKY_TRANSFORM:
+      // TODO(jwcai) Return the default value android::BufferQueue is using as
+      // there is no way dvr::ConsumerQueue can set it.
+      value = 0;
+      break;
+    case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+      // In Daydream's implementation, the consumer end (i.e. VR Compostior)
+      // knows how to handle protected buffers.
+      value = 1;
+      break;
+    default:
+      return BAD_VALUE;
+  }
+
+  ALOGD_IF(TRACE, "query: key=%d, v=%d", what, value);
+  *out_value = value;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::connect(
+    const sp<IProducerListener>& /* listener */, int api,
+    bool /* producer_controlled_by_app */, QueueBufferOutput* output) {
+  // Consumer interaction are actually handled by buffer hub, and we need
+  // to maintain consumer operations here. We only need to perform basic input
+  // parameter checks here.
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  if (output == nullptr) {
+    return BAD_VALUE;
+  }
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (connected_api_ != kNoConnectedApi) {
+    return BAD_VALUE;
+  }
+
+  switch (api) {
+    case NATIVE_WINDOW_API_EGL:
+    case NATIVE_WINDOW_API_CPU:
+    case NATIVE_WINDOW_API_MEDIA:
+    case NATIVE_WINDOW_API_CAMERA:
+      connected_api_ = api;
+
+      output->width = queue_->default_width();
+      output->height = queue_->default_height();
+
+      // default values, we don't use them yet.
+      output->transformHint = 0;
+      output->numPendingBuffers = 0;
+      output->nextFrameNumber = 0;
+      output->bufferReplaced = false;
+
+      break;
+    default:
+      ALOGE("BufferHubQueueProducer::connect: unknow API %d", api);
+      return BAD_VALUE;
+  }
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::disconnect(int api, DisconnectMode /*mode*/) {
+  // Consumer interaction are actually handled by buffer hub, and we need
+  // to maintain consumer operations here.  We only need to perform basic input
+  // parameter checks here.
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  if (kNoConnectedApi == connected_api_) {
+    return NO_INIT;
+  } else if (api != connected_api_) {
+    return BAD_VALUE;
+  }
+
+  connected_api_ = kNoConnectedApi;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setSidebandStream(
+    const sp<NativeHandle>& stream) {
+  if (stream != nullptr) {
+    // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
+    // metadata.
+    ALOGE("SidebandStream is not currently supported.");
+    return INVALID_OPERATION;
+  }
+  return NO_ERROR;
+}
+
+void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */,
+                                             uint32_t /* height */,
+                                             PixelFormat /* format */,
+                                             uint32_t /* usage */) {
+  // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
+  // of buffers permitted by the current BufferQueue configuration (aka
+  // |max_buffer_count_|).
+  ALOGE("BufferHubQueueProducer::allocateBuffers not implemented.");
+}
+
+status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
+  ALOGE("BufferHubQueueProducer::allowAllocation not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setGenerationNumber(
+    uint32_t generation_number) {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+  generation_number_ = generation_number;
+  return NO_ERROR;
+}
+
+String8 BufferHubQueueProducer::getConsumerName() const {
+  // BufferHub based implementation could have one to many producer/consumer
+  // relationship, thus |getConsumerName| from the producer side does not
+  // make any sense.
+  ALOGE("BufferHubQueueProducer::getConsumerName not supported.");
+  return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubQueueProducer::setSharedBufferMode(bool shared_buffer_mode) {
+  if (shared_buffer_mode) {
+    ALOGE(
+        "BufferHubQueueProducer::setSharedBufferMode(true) is not supported.");
+    // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
+    return INVALID_OPERATION;
+  }
+  // Setting to default should just work as a no-op.
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool auto_refresh) {
+  if (auto_refresh) {
+    ALOGE("BufferHubQueueProducer::setAutoRefresh(true) is not supported.");
+    return INVALID_OPERATION;
+  }
+  // Setting to default should just work as a no-op.
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mutex_);
+  dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::getLastQueuedBuffer(
+    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
+    float /*out_transform_matrix*/[16]) {
+  ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+void BufferHubQueueProducer::getFrameTimestamps(
+    FrameEventHistoryDelta* /*outDelta*/) {
+  ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented.");
+}
+
+status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  *out_id = unique_id_;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height,
+                                                uint32_t layer_count,
+                                                PixelFormat format,
+                                                uint64_t usage) {
+  size_t slot;
+
+  if (queue_->AllocateBuffer(width, height, layer_count, format, usage, &slot) <
+      0) {
+    ALOGE("Failed to allocate new buffer in BufferHub.");
+    return NO_MEMORY;
+  }
+
+  auto buffer_producer = queue_->GetBuffer(slot);
+
+  LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
+                      "Failed to get buffer producer at slot: %zu", slot);
+
+  buffers_[slot].mBufferProducer = buffer_producer;
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) {
+  int ret = queue_->DetachBuffer(slot);
+  if (ret < 0) {
+    ALOGE("BufferHubQueueProducer::RemoveBuffer failed through RPC, ret=%s",
+          strerror(-ret));
+    return ret;
+  }
+
+  // Reset in memory objects related the the buffer.
+  buffers_[slot].mBufferProducer = nullptr;
+  buffers_[slot].mGraphicBuffer = nullptr;
+  buffers_[slot].mBufferState.detachProducer();
+  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
new file mode 100644
index 0000000..ed67f79
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -0,0 +1,456 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+
+#include <gui/BufferQueueDefs.h>
+
+#include <pdx/client.h>
+#include <pdx/status.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueue;
+
+// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are
+// automatically re-requeued when released by the remote side.
+class BufferHubQueue : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  virtual ~BufferHubQueue() {}
+  void Initialize();
+
+  // Create a new consumer queue that is attached to the producer. Returns
+  // a new consumer queue client or nullptr on failure.
+  std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
+
+  // Create a new consumer queue that is attached to the producer. This queue
+  // sets each of its imported consumer buffers to the ignored state to avoid
+  // participation in lifecycle events.
+  std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
+
+  // Return the default buffer width of this buffer queue.
+  size_t default_width() const { return default_width_; }
+
+  // Return the default buffer height of this buffer queue.
+  size_t default_height() const { return default_height_; }
+
+  // Return the default buffer format of this buffer queue.
+  int32_t default_format() const { return default_format_; }
+
+  // Create a new consumer in handle form for immediate transport over RPC.
+  Status<LocalChannelHandle> CreateConsumerQueueHandle();
+
+  // Return the number of buffers avaiable for dequeue.
+  size_t count() const { return available_buffers_.GetSize(); }
+
+  // Return the total number of buffers that the queue is tracking.
+  size_t capacity() const { return capacity_; }
+
+  // Return the size of metadata structure associated with this BufferBubQueue.
+  size_t metadata_size() const { return meta_size_; }
+
+  // Return whether the buffer queue is alrady full.
+  bool is_full() const { return available_buffers_.IsFull(); }
+
+  explicit operator bool() const { return epoll_fd_.IsValid(); }
+
+  std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
+    return buffers_[slot];
+  }
+
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
+  // Returns an fd that signals pending queue events using
+  // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
+  // called to handle pending queue events.
+  int queue_fd() const { return epoll_fd_.Get(); }
+
+  // Handles any pending events, returning available buffers to the queue and
+  // reaping disconnected buffers. Returns true if successful, false if an error
+  // occurred.
+  bool HandleQueueEvents() { return WaitForBuffers(0); }
+
+  // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer
+  // and |Acquire|'ed for consumer. This is only used for internal bookkeeping.
+  void Enqueue(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot);
+
+  // |BufferHubQueue| will keep track of at most this value of buffers.
+  static constexpr size_t kMaxQueueCapacity =
+      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+  // Special epoll data field indicating that the epoll event refers to the
+  // queue.
+  static constexpr int64_t kEpollQueueEventIndex = -1;
+
+  // When pass |kNoTimeout| to |Dequeue|, it will block indefinitely without a
+  // timeout.
+  static constexpr int kNoTimeOut = -1;
+
+  int id() const { return id_; }
+  bool hung_up() const { return hung_up_; }
+
+ protected:
+  BufferHubQueue(LocalChannelHandle channel);
+  BufferHubQueue(const std::string& endpoint_path);
+
+  // Imports the queue parameters by querying BufferHub for the parameters for
+  // this channel.
+  Status<void> ImportQueue();
+
+  // Sets up the queue with the given parameters.
+  void SetupQueue(size_t meta_size_bytes_, int id);
+
+  // Called by ProducerQueue::AddBuffer and ConsumerQueue::AddBuffer only. to
+  // register a buffer for epoll and internal bookkeeping.
+  int AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot);
+
+  // Called by ProducerQueue::DetachBuffer and ConsumerQueue::DetachBuffer only.
+  // to deregister a buffer for epoll and internal bookkeeping.
+  virtual int DetachBuffer(size_t slot);
+
+  // 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,
+                                                        LocalHandle* fence);
+
+  // Wait for buffers to be released and re-add them to the queue.
+  bool WaitForBuffers(int timeout);
+  void HandleBufferEvent(size_t slot, int poll_events);
+  void HandleQueueEvent(int poll_events);
+
+  virtual int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+                            LocalHandle* fence) = 0;
+
+  // Called when a buffer is allocated remotely.
+  virtual Status<void> OnBufferAllocated() { return {}; }
+
+  // Data members to handle arbitrary metadata passed through BufferHub. It is
+  // fair to enforce that all buffers in the same queue share the same metadata
+  // type. |meta_size_| is used to store the size of metadata on queue creation;
+  // and |meta_buffer_tmp_| is allocated and resized to |meta_size_| on queue
+  // creation to be later used as temporary space so that we can avoid
+  // additional dynamic memory allocation in each |Enqueue| and |Dequeue| call.
+  size_t meta_size_;
+
+  // Here we intentionally choose |unique_ptr<uint8_t[]>| over vector<uint8_t>
+  // to disallow dynamic resizing for stability reasons.
+  std::unique_ptr<uint8_t[]> meta_buffer_tmp_;
+
+ private:
+  static constexpr size_t kMaxEvents = 128;
+
+  // The |u64| data field of an epoll event is interpreted as int64_t:
+  // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
+  // element of |buffers_| as a direct index;
+  static bool is_buffer_event_index(int64_t index) {
+    return index >= 0 &&
+           index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
+  }
+
+  // When |index| == kEpollQueueEventIndex, it refers to the queue itself.
+  static bool is_queue_event_index(int64_t index) {
+    return index == BufferHubQueue::kEpollQueueEventIndex;
+  }
+
+  struct BufferInfo {
+    // A logical slot number that is assigned to a buffer at allocation time.
+    // The slot number remains unchanged during the entire life cycle of the
+    // buffer and should not be confused with the enqueue and dequeue order.
+    size_t slot;
+
+    // A BufferHubBuffer client.
+    std::shared_ptr<BufferHubBuffer> buffer;
+
+    // Metadata associated with the buffer.
+    std::unique_ptr<uint8_t[]> metadata;
+
+    BufferInfo() : BufferInfo(-1, 0) {}
+
+    BufferInfo(size_t slot, size_t metadata_size)
+        : slot(slot),
+          buffer(nullptr),
+          metadata(metadata_size ? new uint8_t[metadata_size] : nullptr) {}
+
+    BufferInfo(BufferInfo&& other)
+        : slot(other.slot),
+          buffer(std::move(other.buffer)),
+          metadata(std::move(other.metadata)) {}
+
+    BufferInfo& operator=(BufferInfo&& other) {
+      slot = other.slot;
+      buffer = std::move(other.buffer);
+      metadata = std::move(other.metadata);
+      return *this;
+    }
+
+   private:
+    BufferInfo(const BufferInfo&) = delete;
+    void operator=(BufferInfo&) = delete;
+  };
+
+  // Default buffer width that can be set to override the buffer width when a
+  // width and height of 0 are specified in AllocateBuffer.
+  size_t default_width_{1};
+
+  // Default buffer height that can be set to override the buffer height when a
+  // width and height of 0 are specified in AllocateBuffer.
+  size_t default_height_{1};
+
+  // Default buffer format that can be set to override the buffer format when it
+  // isn't specified in AllocateBuffer.
+  int32_t default_format_{PIXEL_FORMAT_RGBA_8888};
+
+  // Buffer queue:
+  // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
+  std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
+
+  // |epollhup_pending_| tracks whether a slot of |buffers_| get detached before
+  // its corresponding EPOLLHUP event got handled. This could happen as the
+  // following sequence:
+  // 1. Producer queue's client side allocates a new buffer (at slot 1).
+  // 2. Producer queue's client side replaces an existing buffer (at slot 0).
+  //    This is implemented by first detaching the buffer and then allocating a
+  //    new buffer.
+  // 3. During the same epoll_wait, Consumer queue's client side gets EPOLLIN
+  //    event on the queue which indicates a new buffer is available and the
+  //    EPOLLHUP event for slot 0. Consumer handles these two events in order.
+  // 4. Consumer client calls BufferHubRPC::ConsumerQueueImportBuffers and both
+  //    slot 0 and (the new) slot 1 buffer will be imported. During the import
+  //    of the buffer at slot 1, consumer client detaches the old buffer so that
+  //    the new buffer can be registered. At the same time
+  //    |epollhup_pending_[slot]| is marked to indicate that buffer at this slot
+  //    was detached prior to EPOLLHUP event.
+  // 5. Consumer client continues to handle the EPOLLHUP. Since
+  //    |epollhup_pending_[slot]| is marked as true, it can safely ignore the
+  //    event without detaching the newly allocated buffer at slot 1.
+  //
+  // In normal situations where the previously described sequence doesn't
+  // happen, an EPOLLHUP event should trigger a regular buffer detach.
+  std::vector<bool> epollhup_pending_;
+
+  // |available_buffers_| uses |dvr::RingBuffer| to implementation queue
+  // sematics. When |Dequeue|, we pop the front element from
+  // |available_buffers_|, and  that buffer's reference count will decrease by
+  // one, while another reference in |buffers_| keeps the last reference to
+  // prevent the buffer from being deleted.
+  RingBuffer<BufferInfo> available_buffers_;
+
+  // Fences (acquire fence for consumer and release fence for consumer) , one
+  // for each buffer slot.
+  std::vector<LocalHandle> fences_;
+
+  // Keep track with how many buffers have been added into the queue.
+  size_t capacity_;
+
+  // Epoll fd used to wait for BufferHub events.
+  EpollFileDescriptor epoll_fd_;
+
+  // Flag indicating that the other side hung up. For ProducerQueues this
+  // triggers when BufferHub dies or explicitly closes the queue channel. For
+  // ConsumerQueues this can either mean the same or that the ProducerQueue on
+  // the other end hung up.
+  bool hung_up_{false};
+
+  // Global id for the queue that is consistent across processes.
+  int id_;
+
+  BufferHubQueue(const BufferHubQueue&) = delete;
+  void operator=(BufferHubQueue&) = delete;
+};
+
+class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
+ public:
+  template <typename Meta>
+  static std::unique_ptr<ProducerQueue> Create() {
+    return BASE::Create(sizeof(Meta));
+  }
+  static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes) {
+    return BASE::Create(meta_size_bytes);
+  }
+
+  // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
+  // in |usage_clear_mask| will be automatically masked off. Note that
+  // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
+  // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
+  // allocation through this producer queue shall not have any of the usage bits
+  // in |usage_deny_set_mask| set. Allocation calls violating this will be
+  // rejected. All buffer allocation through this producer queue must have all
+  // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
+  // this will be rejected. Note that |usage_deny_set_mask| and
+  // |usage_deny_clear_mask| shall not conflict with each other. Such
+  // configuration will be treated as invalid input on creation.
+  template <typename Meta>
+  static std::unique_ptr<ProducerQueue> Create(uint32_t usage_set_mask,
+                                               uint32_t usage_clear_mask,
+                                               uint32_t usage_deny_set_mask,
+                                               uint32_t usage_deny_clear_mask) {
+    return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask,
+                        usage_deny_set_mask, usage_deny_clear_mask);
+  }
+  static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes,
+                                               uint32_t usage_set_mask,
+                                               uint32_t usage_clear_mask,
+                                               uint32_t usage_deny_set_mask,
+                                               uint32_t usage_deny_clear_mask) {
+    return BASE::Create(meta_size_bytes, usage_set_mask, usage_clear_mask,
+                        usage_deny_set_mask, usage_deny_clear_mask);
+  }
+
+  // Import a |ProducerQueue| from a channel handle.
+  static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) {
+    return BASE::Create(std::move(handle));
+  }
+
+  // Get a buffer producer. Note that the method doesn't check whether the
+  // buffer slot has a valid buffer that has been allocated already. When no
+  // buffer has been imported before it returns |nullptr|; otherwise it returns
+  // a shared pointer to a |BufferProducer|.
+  std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const {
+    return std::static_pointer_cast<BufferProducer>(
+        BufferHubQueue::GetBuffer(slot));
+  }
+
+  // Allocate producer buffer to populate the queue. Once allocated, a producer
+  // buffer is automatically enqueue'd into the ProducerQueue and available to
+  // use (i.e. in |Gain|'ed mode).
+  // Returns Zero on success and negative error code when buffer allocation
+  // fails.
+  int AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+                     uint32_t format, uint64_t usage, size_t* out_slot);
+
+  // Add a producer buffer to populate the queue. Once added, a producer buffer
+  // is available to use (i.e. in |Gain|'ed mode).
+  int AddBuffer(const std::shared_ptr<BufferProducer>& buf, size_t slot);
+
+  // Detach producer buffer from the queue.
+  // Returns Zero on success and negative error code when buffer detach
+  // fails.
+  int DetachBuffer(size_t slot) override;
+
+  // 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, LocalHandle* release_fence);
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through ProducerQueue::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+  explicit ProducerQueue(size_t meta_size);
+  ProducerQueue(LocalChannelHandle handle);
+  ProducerQueue(size_t meta_size, uint64_t usage_set_mask,
+                uint64_t usage_clear_mask, uint64_t usage_deny_set_mask,
+                uint64_t usage_deny_clear_mask);
+
+  int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+                    LocalHandle* release_fence) override;
+};
+
+// Explicit specializations of ProducerQueue::Create for void metadata type.
+template <>
+inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>() {
+  return ProducerQueue::Create(0);
+}
+template <>
+inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>(
+    uint32_t usage_set_mask, uint32_t usage_clear_mask,
+    uint32_t usage_deny_set_mask, uint32_t usage_deny_clear_mask) {
+  return ProducerQueue::Create(0, usage_set_mask, usage_clear_mask,
+                               usage_deny_set_mask, usage_deny_clear_mask);
+}
+
+class ConsumerQueue : public BufferHubQueue {
+ public:
+  // Get a buffer consumer. Note that the method doesn't check whether the
+  // buffer slot has a valid buffer that has been imported already. When no
+  // buffer has been imported before it returns nullptr; otherwise returns a
+  // shared pointer to a BufferConsumer.
+  std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const {
+    return std::static_pointer_cast<BufferConsumer>(
+        BufferHubQueue::GetBuffer(slot));
+  }
+
+  // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
+  // whether or not buffers are set to be ignored when imported. This may be
+  // 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(LocalChannelHandle handle,
+                                               bool ignore_on_import = false) {
+    return std::unique_ptr<ConsumerQueue>(
+        new ConsumerQueue(std::move(handle), ignore_on_import));
+  }
+
+  // Import newly created buffers from the service side.
+  // Returns number of buffers successfully imported or an error.
+  Status<size_t> ImportBuffers();
+
+  // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
+  // mode, and caller should call Releasse() once it's done writing to release
+  // the buffer to the producer side. |meta| is passed along from BufferHub,
+  // The user of BufferProducer is responsible with making sure that the
+  // Dequeue() is done with the corect metadata type and size with those used
+  // when the buffer is orignally created.
+  template <typename Meta>
+  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+      int timeout, size_t* slot, Meta* meta, LocalHandle* acquire_fence) {
+    return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
+  }
+  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+      int timeout, size_t* slot, LocalHandle* acquire_fence) {
+    return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
+  }
+
+  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+      int timeout, size_t* slot, void* meta, size_t meta_size,
+      LocalHandle* acquire_fence);
+
+ private:
+  friend BufferHubQueue;
+
+  ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import = false);
+
+  // 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|
+  // will catch the |Post| and |Acquire| the buffer to make it available for
+  // consumer.
+  int AddBuffer(const std::shared_ptr<BufferConsumer>& buf, size_t slot);
+
+  int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+                    LocalHandle* acquire_fence) override;
+
+  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
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
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
new file mode 100644
index 0000000..7890176
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -0,0 +1,183 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueProducer : public BnGraphicBufferProducer {
+ public:
+  static constexpr int kNoConnectedApi = -1;
+
+  // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
+  // side logic doesn't limit the number of buffer it can acquire
+  // simultaneously. We need a way for consumer logic to configure and enforce
+  // that.
+  static constexpr int kDefaultUndequeuedBuffers = 1;
+
+  // Create a BufferHubQueueProducer instance by creating a new producer queue.
+  static sp<BufferHubQueueProducer> Create();
+
+  // Create a BufferHubQueueProducer instance by importing an existing prodcuer
+  // queue.
+  static sp<BufferHubQueueProducer> Create(
+      const std::shared_ptr<ProducerQueue>& producer);
+
+  // See |IGraphicBufferProducer::requestBuffer|
+  status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+
+  // For the BufferHub based implementation. All buffers in the queue are
+  // allowed to be dequeued from the consumer side. It call always returns
+  // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
+  // |max_dequeued_buffers| here can be considered the same as setting queue
+  // capacity.
+  //
+  // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
+  status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
+
+  // See |IGraphicBufferProducer::setAsyncMode|
+  status_t setAsyncMode(bool async) override;
+
+  // See |IGraphicBufferProducer::dequeueBuffer|
+  status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
+                         uint32_t height, PixelFormat format, uint32_t usage,
+                         FrameEventHistoryDelta* outTimestamps) override;
+
+  // See |IGraphicBufferProducer::detachBuffer|
+  status_t detachBuffer(int slot) override;
+
+  // See |IGraphicBufferProducer::detachNextBuffer|
+  status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer,
+                            sp<Fence>* out_fence) override;
+
+  // See |IGraphicBufferProducer::attachBuffer|
+  status_t attachBuffer(int* out_slot,
+                        const sp<GraphicBuffer>& buffer) override;
+
+  // See |IGraphicBufferProducer::queueBuffer|
+  status_t queueBuffer(int slot, const QueueBufferInput& input,
+                       QueueBufferOutput* output) override;
+
+  // See |IGraphicBufferProducer::cancelBuffer|
+  status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+
+  // See |IGraphicBufferProducer::query|
+  status_t query(int what, int* out_value) override;
+
+  // See |IGraphicBufferProducer::connect|
+  status_t connect(const sp<IProducerListener>& listener, int api,
+                   bool producer_controlled_by_app,
+                   QueueBufferOutput* output) override;
+
+  // See |IGraphicBufferProducer::disconnect|
+  status_t disconnect(int api,
+                      DisconnectMode mode = DisconnectMode::Api) override;
+
+  // See |IGraphicBufferProducer::setSidebandStream|
+  status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+
+  // See |IGraphicBufferProducer::allocateBuffers|
+  void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+                       uint32_t usage) override;
+
+  // See |IGraphicBufferProducer::allowAllocation|
+  status_t allowAllocation(bool allow) override;
+
+  // See |IGraphicBufferProducer::setGenerationNumber|
+  status_t setGenerationNumber(uint32_t generation_number) override;
+
+  // See |IGraphicBufferProducer::getConsumerName|
+  String8 getConsumerName() const override;
+
+  // See |IGraphicBufferProducer::setSharedBufferMode|
+  status_t setSharedBufferMode(bool shared_buffer_mode) override;
+
+  // See |IGraphicBufferProducer::setAutoRefresh|
+  status_t setAutoRefresh(bool auto_refresh) override;
+
+  // See |IGraphicBufferProducer::setDequeueTimeout|
+  status_t setDequeueTimeout(nsecs_t timeout) override;
+
+  // See |IGraphicBufferProducer::getLastQueuedBuffer|
+  status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer,
+                               sp<Fence>* out_fence,
+                               float out_transform_matrix[16]) override;
+
+  // See |IGraphicBufferProducer::getFrameTimestamps|
+  void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
+
+  // See |IGraphicBufferProducer::getUniqueId|
+  status_t getUniqueId(uint64_t* out_id) const override;
+
+ private:
+  using LocalHandle = pdx::LocalHandle;
+
+  // Private constructor to force use of |Create|.
+  BufferHubQueueProducer() {}
+
+  static uint64_t genUniqueId() {
+    static std::atomic<uint32_t> counter{0};
+    static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+    return id | counter++;
+  }
+
+  // Allocate new buffer through BufferHub and add it into |queue_| for
+  // bookkeeping.
+  status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+                          PixelFormat format, uint64_t usage);
+
+  // Remove a buffer via BufferHubRPC.
+  status_t RemoveBuffer(size_t slot);
+
+  // Concreate implementation backed by BufferHubBuffer.
+  std::shared_ptr<ProducerQueue> queue_;
+
+  // Mutex for thread safety.
+  std::mutex mutex_;
+
+  // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
+  int connected_api_{kNoConnectedApi};
+
+  // |max_buffer_count_| sets the capacity of the underlying buffer queue.
+  int32_t max_buffer_count_{BufferHubQueue::kMaxQueueCapacity};
+
+  // |max_dequeued_buffer_count_| set the maximum number of buffers that can
+  // be dequeued at the same momment.
+  int32_t max_dequeued_buffer_count_{1};
+
+  // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
+  // slot is not yet available. The timeout is stored in milliseconds.
+  int dequeue_timeout_ms_{BufferHubQueue::kNoTimeOut};
+
+  // |generation_number_| stores the current generation number of the attached
+  // producer. Any attempt to attach a buffer with a different generation
+  // number will fail.
+  // TOOD(b/38137191) Currently not used as we don't support
+  // IGraphicBufferProducer::detachBuffer.
+  uint32_t generation_number_{0};
+
+  // |buffers_| stores the buffers that have been dequeued from
+  // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
+  // filled in with the result of |Dequeue|.
+  // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
+  // requested buffer usage or geometry differs from that of the buffer
+  // allocated to a slot.
+  struct BufferHubSlot : public BufferSlot {
+    BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+    // BufferSlot comes from android framework, using m prefix to comply with
+    // the name convention with the reset of data fields from BufferSlot.
+    std::shared_ptr<BufferProducer> mBufferProducer;
+    bool mIsReallocating;
+  };
+  BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity];
+
+  // A uniqueId used by IGraphicBufferProducer interface.
+  const uint64_t unique_id_{genUniqueId()};
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
new file mode 100644
index 0000000..865573c
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -0,0 +1,48 @@
+
+
+shared_libraries = [
+    "libbase",
+    "libbinder",
+    "libcutils",
+    "libgui",
+    "liblog",
+    "libhardware",
+    "libui",
+    "libutils",
+]
+
+static_libraries = [
+    "libbufferhubqueue",
+    "libbufferhub",
+    "libchrome",
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+cc_test {
+    srcs: ["buffer_hub_queue-test.cpp"],
+    static_libs: static_libraries,
+    shared_libs: shared_libraries,
+    cflags: [
+        "-DLOG_TAG=\"buffer_hub_queue-test\"",
+        "-DTRACE=0",
+        "-O0",
+        "-g",
+    ],
+    name: "buffer_hub_queue-test",
+    tags: ["optional"],
+}
+
+cc_test {
+    srcs: ["buffer_hub_queue_producer-test.cpp"],
+    static_libs: static_libraries,
+    shared_libs: shared_libraries,
+    cflags: [
+        "-DLOG_TAG=\"buffer_hub_queue_producer-test\"",
+        "-DTRACE=0",
+        "-O0",
+        "-g",
+    ],
+    name: "buffer_hub_queue_producer-test",
+    tags: ["optional"],
+}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
new file mode 100644
index 0000000..fe0b12a
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -0,0 +1,421 @@
+#include <base/logging.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+using pdx::LocalHandle;
+
+namespace {
+
+constexpr int kBufferWidth = 100;
+constexpr int kBufferHeight = 1;
+constexpr int kBufferLayerCount = 1;
+constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+
+class BufferHubQueueTest : public ::testing::Test {
+ public:
+  template <typename Meta>
+  bool CreateProducerQueue(uint64_t usage_set_mask = 0,
+                           uint64_t usage_clear_mask = 0,
+                           uint64_t usage_deny_set_mask = 0,
+                           uint64_t usage_deny_clear_mask = 0) {
+    producer_queue_ =
+        ProducerQueue::Create<Meta>(usage_set_mask, usage_clear_mask,
+                                    usage_deny_set_mask, usage_deny_clear_mask);
+    return producer_queue_ != nullptr;
+  }
+
+  bool CreateConsumerQueue() {
+    if (producer_queue_) {
+      consumer_queue_ = producer_queue_->CreateConsumerQueue();
+      return consumer_queue_ != nullptr;
+    } else {
+      return false;
+    }
+  }
+
+  template <typename Meta>
+  bool CreateQueues(int usage_set_mask = 0, int usage_clear_mask = 0,
+                    int usage_deny_set_mask = 0,
+                    int usage_deny_clear_mask = 0) {
+    return CreateProducerQueue<Meta>(usage_set_mask, usage_clear_mask,
+                                     usage_deny_set_mask,
+                                     usage_deny_clear_mask) &&
+           CreateConsumerQueue();
+  }
+
+  void AllocateBuffer() {
+    // Create producer buffer.
+    size_t slot;
+    int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                              kBufferLayerCount, kBufferFormat,
+                                              kBufferUsage, &slot);
+    ASSERT_EQ(ret, 0);
+  }
+
+ protected:
+  std::unique_ptr<ProducerQueue> producer_queue_;
+  std::unique_ptr<ConsumerQueue> consumer_queue_;
+};
+
+TEST_F(BufferHubQueueTest, TestDequeue) {
+  const size_t nb_dequeue_times = 16;
+
+  ASSERT_TRUE(CreateQueues<size_t>());
+
+  // Allocate only one buffer.
+  AllocateBuffer();
+
+  // But dequeue multiple times.
+  for (size_t i = 0; i < nb_dequeue_times; i++) {
+    size_t slot;
+    LocalHandle fence;
+    auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+    ASSERT_TRUE(p1_status.ok());
+    auto p1 = p1_status.take();
+    ASSERT_NE(nullptr, p1);
+    size_t mi = i;
+    ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0);
+    size_t mo;
+    auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
+    ASSERT_TRUE(c1_status.ok());
+    auto c1 = c1_status.take();
+    ASSERT_NE(nullptr, c1);
+    ASSERT_EQ(mi, mo);
+    c1->Release(LocalHandle());
+  }
+}
+
+TEST_F(BufferHubQueueTest, TestProducerConsumer) {
+  const size_t nb_buffer = 16;
+  size_t slot;
+  uint64_t seq;
+
+  ASSERT_TRUE(CreateQueues<uint64_t>());
+
+  for (size_t i = 0; i < nb_buffer; i++) {
+    AllocateBuffer();
+
+    // Producer queue has all the available buffers on initialize.
+    ASSERT_EQ(producer_queue_->count(), i + 1);
+    ASSERT_EQ(producer_queue_->capacity(), i + 1);
+
+    // Consumer queue has no avaiable buffer on initialize.
+    ASSERT_EQ(consumer_queue_->count(), 0U);
+    // Consumer queue does not import buffers until a dequeue is issued.
+    ASSERT_EQ(consumer_queue_->capacity(), i);
+    // 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);
+    ASSERT_FALSE(status.ok());
+    ASSERT_EQ(ETIMEDOUT, status.error());
+    ASSERT_EQ(consumer_queue_->capacity(), i + 1);
+  }
+
+  for (size_t i = 0; i < nb_buffer; i++) {
+    LocalHandle fence;
+    // First time, there is no buffer available to dequeue.
+    auto consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    ASSERT_FALSE(consumer_status.ok());
+    ASSERT_EQ(ETIMEDOUT, consumer_status.error());
+
+    // Make sure Producer buffer is Post()'ed so that it's ready to Accquire
+    // in the consumer's Dequeue() function.
+    auto producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+    ASSERT_TRUE(producer_status.ok());
+    auto producer = producer_status.take();
+    ASSERT_NE(nullptr, producer);
+
+    uint64_t seq_in = static_cast<uint64_t>(i);
+    ASSERT_EQ(producer->Post({}, &seq_in, sizeof(seq_in)), 0);
+
+    // Second time, the just |Post()|'ed buffer should be dequeued.
+    uint64_t seq_out = 0;
+    consumer_status = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence);
+    ASSERT_TRUE(consumer_status.ok());
+    auto consumer = consumer_status.take();
+    ASSERT_NE(nullptr, consumer);
+    ASSERT_EQ(seq_in, seq_out);
+  }
+}
+
+TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
+  ASSERT_TRUE(CreateProducerQueue<void>());
+
+  // Allocate buffers.
+  const size_t kBufferCount = 4u;
+  for (size_t i = 0; i < kBufferCount; i++) {
+    AllocateBuffer();
+  }
+  ASSERT_EQ(kBufferCount, producer_queue_->count());
+
+  // Build a silent consumer queue to test multi-consumer queue features.
+  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());
+
+  // Dequeue and post a buffer.
+  size_t slot;
+  LocalHandle fence;
+  auto producer_status = producer_queue_->Dequeue(0, &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>({}));
+
+  // Currently we expect no buffer to be available prior to calling
+  // WaitForBuffers/HandleQueueEvents.
+  // TODO(eieio): Note this behavior may change in the future.
+  EXPECT_EQ(0u, silent_queue->count());
+  EXPECT_FALSE(silent_queue->HandleQueueEvents());
+  EXPECT_EQ(0u, silent_queue->count());
+
+  // Build a new consumer queue to test multi-consumer queue features.
+  consumer_queue_ = silent_queue->CreateConsumerQueue();
+  ASSERT_NE(nullptr, consumer_queue_);
+
+  // Check that buffers are correctly imported on construction.
+  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
+  EXPECT_EQ(1u, consumer_queue_->count());
+
+  // Reclaim released/ignored buffers.
+  producer_queue_->HandleQueueEvents();
+  ASSERT_EQ(kBufferCount - 1, producer_queue_->count());
+
+  // Post another buffer.
+  producer_status = producer_queue_->Dequeue(0, &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());
+
+  // Dequeue and acquire/release (discard) buffers on the consumer end.
+  auto consumer_status = consumer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_TRUE(consumer_status.ok());
+  auto consumer_buffer = consumer_status.take();
+  ASSERT_NE(nullptr, consumer_buffer);
+  consumer_buffer->Discard();
+
+  // Buffer should be returned to the producer queue without being handled by
+  // the silent consumer queue.
+  EXPECT_EQ(1u, consumer_queue_->count());
+  EXPECT_EQ(kBufferCount - 2, producer_queue_->count());
+  EXPECT_TRUE(producer_queue_->HandleQueueEvents());
+  EXPECT_EQ(kBufferCount - 1, producer_queue_->count());
+}
+
+struct TestMetadata {
+  char a;
+  int32_t b;
+  int64_t c;
+};
+
+TEST_F(BufferHubQueueTest, TestMetadata) {
+  ASSERT_TRUE(CreateQueues<TestMetadata>());
+  AllocateBuffer();
+
+  std::vector<TestMetadata> ms = {
+      {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
+
+  for (auto mi : ms) {
+    size_t slot;
+    LocalHandle fence;
+    auto p1_status = producer_queue_->Dequeue(0, &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);
+    ASSERT_TRUE(c1_status.ok());
+    auto c1 = c1_status.take();
+    ASSERT_EQ(mi.a, mo.a);
+    ASSERT_EQ(mi.b, mo.b);
+    ASSERT_EQ(mi.c, mo.c);
+    c1->Release(LocalHandle(-1));
+  }
+}
+
+TEST_F(BufferHubQueueTest, TestMetadataMismatch) {
+  ASSERT_TRUE(CreateQueues<int64_t>());
+  AllocateBuffer();
+
+  int64_t mi = 3;
+  size_t slot;
+  LocalHandle fence;
+  auto p1_status = producer_queue_->Dequeue(0, &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);
+
+  int32_t mo;
+  // Acquire a buffer with mismatched metadata is not OK.
+  auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+  ASSERT_FALSE(c1_status.ok());
+}
+
+TEST_F(BufferHubQueueTest, TestEnqueue) {
+  ASSERT_TRUE(CreateQueues<int64_t>());
+  AllocateBuffer();
+
+  size_t slot;
+  LocalHandle fence;
+  auto p1_status = producer_queue_->Dequeue(0, &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);
+  ASSERT_FALSE(c1_status.ok());
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
+  ASSERT_TRUE(CreateQueues<int64_t>());
+
+  size_t s1;
+  AllocateBuffer();
+  LocalHandle fence;
+  auto p1_status = producer_queue_->Dequeue(0, &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);
+  ASSERT_FALSE(p2_status.ok());
+  ASSERT_EQ(ETIMEDOUT, p2_status.error());
+
+  // dynamically add buffer.
+  AllocateBuffer();
+  ASSERT_EQ(producer_queue_->count(), 1U);
+  ASSERT_EQ(producer_queue_->capacity(), 2U);
+
+  // now we can dequeue again
+  p2_status = producer_queue_->Dequeue(0, &s2, &fence);
+  ASSERT_TRUE(p2_status.ok());
+  auto p2 = p2_status.take();
+  ASSERT_NE(nullptr, p2);
+  ASSERT_EQ(producer_queue_->count(), 0U);
+  // p1 and p2 should have different slot number
+  ASSERT_NE(s1, s2);
+
+  // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
+  // are called. So far consumer_queue_ should be empty.
+  ASSERT_EQ(consumer_queue_->count(), 0U);
+
+  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);
+  ASSERT_TRUE(c1_status.ok());
+  auto c1 = c1_status.take();
+  ASSERT_NE(nullptr, c1);
+  ASSERT_EQ(consumer_queue_->count(), 0U);
+  ASSERT_EQ(consumer_queue_->capacity(), 2U);
+  ASSERT_EQ(cs1, s1);
+
+  ASSERT_EQ(p2->Post(LocalHandle(), seq), 0);
+  auto c2_status = consumer_queue_->Dequeue(0, &cs2, &seq, &fence);
+  ASSERT_TRUE(c2_status.ok());
+  auto c2 = c2_status.take();
+  ASSERT_NE(nullptr, c2);
+  ASSERT_EQ(cs2, s2);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageSetMask) {
+  const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  ASSERT_TRUE(CreateQueues<int64_t>(set_mask, 0, 0, 0));
+
+  // When allocation, leave out |set_mask| from usage bits on purpose.
+  size_t slot;
+  int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                            kBufferFormat, kBufferLayerCount,
+                                            kBufferUsage & ~set_mask, &slot);
+  ASSERT_EQ(0, ret);
+
+  LocalHandle fence;
+  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_TRUE(p1_status.ok());
+  auto p1 = p1_status.take();
+  ASSERT_EQ(p1->usage() & set_mask, set_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageClearMask) {
+  const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  ASSERT_TRUE(CreateQueues<int64_t>(0, clear_mask, 0, 0));
+
+  // When allocation, add |clear_mask| into usage bits on purpose.
+  size_t slot;
+  int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                            kBufferLayerCount, kBufferFormat,
+                                            kBufferUsage | clear_mask, &slot);
+  ASSERT_EQ(0, ret);
+
+  LocalHandle fence;
+  auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_TRUE(p1_status.ok());
+  auto p1 = p1_status.take();
+  ASSERT_EQ(0u, p1->usage() & clear_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
+  const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  ASSERT_TRUE(CreateQueues<int64_t>(0, 0, deny_set_mask, 0));
+
+  // Now that |deny_set_mask| is illegal, allocation without those bits should
+  // be able to succeed.
+  size_t slot;
+  int ret = producer_queue_->AllocateBuffer(
+      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+      kBufferUsage & ~deny_set_mask, &slot);
+  ASSERT_EQ(ret, 0);
+
+  // While allocation with those bits should fail.
+  ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                        kBufferLayerCount, kBufferFormat,
+                                        kBufferUsage | deny_set_mask, &slot);
+  ASSERT_EQ(ret, -EINVAL);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
+  const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  ASSERT_TRUE(CreateQueues<int64_t>(0, 0, 0, deny_clear_mask));
+
+  // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
+  // mandatory), allocation with those bits should be able to succeed.
+  size_t slot;
+  int ret = producer_queue_->AllocateBuffer(
+      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+      kBufferUsage | deny_clear_mask, &slot);
+  ASSERT_EQ(ret, 0);
+
+  // While allocation without those bits should fail.
+  ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                        kBufferLayerCount, kBufferFormat,
+                                        kBufferUsage & ~deny_clear_mask, &slot);
+  ASSERT_EQ(ret, -EINVAL);
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
new file mode 100644
index 0000000..2b6239f
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -0,0 +1,512 @@
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include <base/logging.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// Default dimensions before setDefaultBufferSize is called by the consumer.
+constexpr uint32_t kDefaultWidth = 1;
+constexpr uint32_t kDefaultHeight = 1;
+
+// Default format before setDefaultBufferFormat is called by the consumer.
+constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+constexpr int kDefaultConsumerUsageBits = 0;
+
+// Default transform hint before setTransformHint is called by the consumer.
+constexpr uint32_t kDefaultTransformHint = 0;
+
+constexpr int kTestApi = NATIVE_WINDOW_API_CPU;
+constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL;
+constexpr int kTestApiInvalid = 0xDEADBEEF;
+constexpr int kTestProducerUsageBits = 0;
+constexpr bool kTestControlledByApp = true;
+
+// Builder pattern to slightly vary *almost* correct input
+// -- avoids copying and pasting
+struct QueueBufferInputBuilder {
+  IGraphicBufferProducer::QueueBufferInput build() {
+    return IGraphicBufferProducer::QueueBufferInput(
+        mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode,
+        mTransform, mFence);
+  }
+
+  QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
+    this->mTimestamp = timestamp;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
+    this->mIsAutoTimestamp = isAutoTimestamp;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) {
+    this->mDataSpace = dataSpace;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setCrop(Rect crop) {
+    this->mCrop = crop;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setScalingMode(int scalingMode) {
+    this->mScalingMode = scalingMode;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setTransform(uint32_t transform) {
+    this->mTransform = transform;
+    return *this;
+  }
+
+  QueueBufferInputBuilder& setFence(sp<Fence> fence) {
+    this->mFence = fence;
+    return *this;
+  }
+
+ private:
+  int64_t mTimestamp{1384888611};
+  bool mIsAutoTimestamp{false};
+  android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN};
+  Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)};
+  int mScalingMode{0};
+  uint32_t mTransform{0};
+  sp<Fence> mFence{Fence::NO_FENCE};
+};
+
+// This is a test that covers our implementation of bufferhubqueue-based
+// IGraphicBufferProducer.
+class BufferHubQueueProducerTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    const ::testing::TestInfo* const testInfo =
+        ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(),
+             testInfo->name());
+
+    mProducer = BufferHubQueueProducer::Create();
+    ASSERT_TRUE(mProducer != nullptr);
+    mSurface = new Surface(mProducer, true);
+    ASSERT_TRUE(mSurface != nullptr);
+  }
+
+  // Connect to a producer in a 'correct' fashion.
+  void ConnectProducer() {
+    IGraphicBufferProducer::QueueBufferOutput output;
+    // Can connect the first time.
+    ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
+                                           kTestControlledByApp, &output));
+  }
+
+  // Dequeue a buffer in a 'correct' fashion.
+  //   Precondition: Producer is connected.
+  void DequeueBuffer(int* outSlot) {
+    sp<Fence> fence;
+    ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence));
+  }
+
+  void DequeueBuffer(int* outSlot, sp<Fence>* outFence) {
+    ASSERT_NE(nullptr, outSlot);
+    ASSERT_NE(nullptr, outFence);
+
+    int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth,
+                                       kDefaultHeight, kDefaultFormat,
+                                       kTestProducerUsageBits, nullptr);
+    // BUFFER_NEEDS_REALLOCATION can be either on or off.
+    ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
+
+    // Slot number should be in boundary.
+    ASSERT_LE(0, *outSlot);
+    ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot);
+  }
+
+  // Create a generic "valid" input for queueBuffer
+  // -- uses the default buffer format, width, etc.
+  static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
+    return QueueBufferInputBuilder().build();
+  }
+
+  const sp<IProducerListener> kDummyListener{new DummyProducerListener};
+
+  sp<BufferHubQueueProducer> mProducer;
+  sp<Surface> mSurface;
+};
+
+TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) {
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  // NULL output returns BAD_VALUE
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+                                          kTestControlledByApp, nullptr));
+
+  // Invalid API returns bad value
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
+                                          kTestControlledByApp, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  // Can't connect when there is already a producer connected.
+  IGraphicBufferProducer::QueueBufferOutput output;
+  EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+                                          kTestControlledByApp, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+}
+
+TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  // Must disconnect with same API number
+  EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther));
+  // API must not be out of range
+  EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid));
+}
+
+TEST_F(BufferHubQueueProducerTest, Query_Succeeds) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  int32_t value = -1;
+  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+  EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
+
+  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+  EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
+
+  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+  EXPECT_EQ(kDefaultFormat, value);
+
+  EXPECT_EQ(NO_ERROR,
+            mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+  EXPECT_LE(0, value);
+  EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, static_cast<size_t>(value));
+
+  EXPECT_EQ(NO_ERROR,
+            mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
+  EXPECT_FALSE(value);  // Can't run behind when we haven't touched the queue
+
+  EXPECT_EQ(NO_ERROR,
+            mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+  EXPECT_EQ(kDefaultConsumerUsageBits, value);
+}
+
+TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  // One past the end of the last 'query' enum value. Update this if we add more
+  // enums.
+  const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1;
+
+  int value;
+  // What was out of range
+  EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value));
+  EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value));
+  EXPECT_EQ(BAD_VALUE,
+            mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value));
+
+  // Some enums from window.h are 'invalid'
+  EXPECT_EQ(BAD_VALUE,
+            mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value));
+  EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value));
+  EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value));
+  EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value));
+  EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
+
+  // Value was NULL
+  EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL));
+}
+
+TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) {
+  int slot = -1;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  // Request the buffer (pre-requisite for queueing)
+  sp<GraphicBuffer> buffer;
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  // A generic "valid" input
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  // Queue the buffer back into the BQ
+  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+
+  EXPECT_EQ(kDefaultWidth, output.width);
+  EXPECT_EQ(kDefaultHeight, output.height);
+  EXPECT_EQ(kDefaultTransformHint, output.transformHint);
+
+  // BufferHubQueue delivers buffers to consumer immediately.
+  EXPECT_EQ(0u, output.numPendingBuffers);
+
+  // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to
+  // be a SurfaceFlinger specific optimization.
+  EXPECT_EQ(0u, output.nextFrameNumber);
+
+  // Buffer was not in the dequeued state
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test invalid slot number
+TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  // A generic "valid" input
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output));
+  EXPECT_EQ(BAD_VALUE,
+            mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output));
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS,
+                                              input, &output));
+}
+
+// Slot was not in the dequeued state (all slots start out in Free state)
+TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output));
+}
+
+// Slot was enqueued without requesting a buffer
+TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) {
+  int slot = -1;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test when fence was NULL
+TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) {
+  int slot = -1;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  sp<GraphicBuffer> buffer;
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  sp<Fence> nullFence = NULL;
+
+  IGraphicBufferProducer::QueueBufferInput input =
+      QueueBufferInputBuilder().setFence(nullFence).build();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test scaling mode was invalid
+TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) {
+  int slot = -1;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  sp<GraphicBuffer> buffer;
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  IGraphicBufferProducer::QueueBufferInput input =
+      QueueBufferInputBuilder().setScalingMode(-1).build();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+
+  input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test crop rect is out of bounds of the buffer dimensions
+TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) {
+  int slot = -1;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  sp<GraphicBuffer> buffer;
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  IGraphicBufferProducer::QueueBufferInput input =
+      QueueBufferInputBuilder()
+          .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1))
+          .build();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) {
+  int slot = -1;
+  sp<Fence> fence;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
+
+  // Should be able to cancel buffer after a dequeue.
+  EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+}
+
+TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
+  return;
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  int minUndequeuedBuffers;
+  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                       &minUndequeuedBuffers));
+
+  const int minBuffers = 1;
+  const int maxBuffers =
+      BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
+      << "async mode: " << false;
+  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+      << "bufferCount: " << minBuffers;
+
+  // Should now be able to dequeue up to minBuffers times
+  // Should now be able to dequeue up to maxBuffers times
+  int slot = -1;
+  for (int i = 0; i < minBuffers; ++i) {
+    ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+  }
+
+  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+
+  // queue the first buffer to enable max dequeued buffer count checking
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+  sp<GraphicBuffer> buffer;
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+
+  sp<Fence> fence;
+  for (int i = 0; i < maxBuffers; ++i) {
+    ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
+  }
+
+  // Cancel a buffer, so we can decrease the buffer count
+  ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+
+  // Should now be able to decrease the max dequeued count by 1
+  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+}
+
+TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+  int minUndequeuedBuffers;
+  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                       &minUndequeuedBuffers));
+
+  const int minBuffers = 1;
+  const int maxBuffers =
+      BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
+      << "async mode: " << false;
+  // Buffer count was out of range
+  EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
+      << "bufferCount: " << 0;
+  EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
+      << "bufferCount: " << maxBuffers + 1;
+
+  // Set max dequeue count to 2
+  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+  // Dequeue 2 buffers
+  int slot = -1;
+  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)))
+        << "slot: " << slot;
+  }
+
+  // Client has too many buffers dequeued
+  EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1))
+      << "bufferCount: " << minBuffers;
+}
+
+TEST_F(BufferHubQueueProducerTest,
+       DisconnectedProducerReturnsError_dequeueBuffer) {
+  int slot = -1;
+  sp<Fence> fence;
+
+  ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
+                                              kDefaultHeight, kDefaultFormat,
+                                              kTestProducerUsageBits, nullptr));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+       DisconnectedProducerReturnsError_requestBuffer) {
+  int slot = -1;
+  sp<GraphicBuffer> buffer;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+  // Shouldn't be able to request buffer after disconnect.
+  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+       DisconnectedProducerReturnsError_queueBuffer) {
+  int slot = -1;
+  sp<GraphicBuffer> buffer;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  // A generic "valid" input
+  IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+  IGraphicBufferProducer::QueueBufferOutput output;
+
+  // Shouldn't be able to queue buffer after disconnect.
+  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+       DisconnectedProducerReturnsError_cancelBuffer) {
+  int slot = -1;
+  sp<GraphicBuffer> buffer;
+
+  ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+  ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+  // Shouldn't be able to cancel buffer after disconnect.
+  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
new file mode 100644
index 0000000..d90521a
--- /dev/null
+++ b/libs/vr/libdisplay/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "display_client.cpp",
+    "display_manager_client.cpp",
+    "display_protocol.cpp",
+    "vsync_client.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libutils",
+    "libui",
+    "libgui",
+    "libhardware",
+    "libsync",
+    "libnativewindow",
+]
+
+staticLibraries = [
+    "libdvrcommon",
+    "libbufferhubqueue",
+    "libbufferhub",
+    "libvrsensor",
+    "libpdx_default_transport",
+]
+
+headerLibraries = [
+    "vulkan_headers",
+]
+
+cc_library {
+    tags: ["tests"],
+    srcs: sourceFiles,
+    cflags: ["-DLOG_TAG=\"libdisplay\"",
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],  // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
+    export_include_dirs: localIncludeFiles,
+    shared_libs: sharedLibraries,
+    static_libs: staticLibraries,
+    header_libs: headerLibraries,
+    export_header_lib_headers: headerLibraries,
+
+    name: "libdisplay",
+}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
new file mode 100644
index 0000000..935ca2e
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,210 @@
+#include "include/private/dvr/display_client.h"
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/status.h>
+
+#include <mutex>
+
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/native_buffer.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+namespace display {
+
+Surface::Surface(LocalChannelHandle channel_handle, int* error)
+    : BASE{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))} {
+  auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
+  if (!status) {
+    ALOGE("Surface::Surface: Failed to get surface info: %s",
+          status.GetErrorMessage().c_str());
+    Close(status.error());
+    if (error)
+      *error = status.error();
+  }
+
+  surface_id_ = status.get().surface_id;
+  z_order_ = status.get().z_order;
+  visible_ = status.get().visible;
+}
+
+Surface::Surface(const SurfaceAttributes& attributes, int* error)
+    : BASE{pdx::default_transport::ClientChannelFactory::Create(
+               DisplayProtocol::kClientPath),
+           kInfiniteTimeout} {
+  auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
+  if (!status) {
+    ALOGE("Surface::Surface: Failed to create display surface: %s",
+          status.GetErrorMessage().c_str());
+    Close(status.error());
+    if (error)
+      *error = status.error();
+  }
+
+  surface_id_ = status.get().surface_id;
+  z_order_ = status.get().z_order;
+  visible_ = status.get().visible;
+}
+
+Status<void> Surface::SetVisible(bool visible) {
+  return SetAttributes(
+      {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
+}
+
+Status<void> Surface::SetZOrder(int z_order) {
+  return SetAttributes(
+      {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
+}
+
+Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
+  auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
+  if (!status) {
+    ALOGE(
+        "Surface::SetAttributes: Failed to set display surface "
+        "attributes: %s",
+        status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  // Set the local cached copies of the attributes we care about from the full
+  // set of attributes sent to the display service.
+  for (const auto& attribute : attributes) {
+    const auto& key = attribute.first;
+    const auto* variant = &attribute.second;
+    bool invalid_value = false;
+    switch (key) {
+      case SurfaceAttribute::Visible:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
+        break;
+      case SurfaceAttribute::ZOrder:
+        invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "Surface::SetAttributes: Failed to set display surface "
+          "attribute %d because of incompatible type: %d",
+          key, variant->index());
+    }
+  }
+
+  return {};
+}
+
+Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue() {
+  ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
+  auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(0);
+  if (!status) {
+    ALOGE("Surface::CreateQueue: Failed to create queue: %s",
+          status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  auto producer_queue = ProducerQueue::Import(status.take());
+  if (!producer_queue) {
+    ALOGE("Surface::CreateQueue: Failed to import producer queue!");
+    return ErrorStatus(ENOMEM);
+  }
+
+  return {std::move(producer_queue)};
+}
+
+Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
+    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+    uint64_t usage, size_t capacity) {
+  ALOGD_IF(TRACE,
+           "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
+           "usage=%" PRIx64 " capacity=%zu",
+           width, height, layer_count, format, usage, capacity);
+  auto status = CreateQueue();
+  if (!status)
+    return status.error_status();
+
+  auto producer_queue = status.take();
+
+  ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
+  for (size_t i = 0; i < capacity; i++) {
+    size_t slot;
+    const int ret = producer_queue->AllocateBuffer(width, height, layer_count,
+                                                   format, usage, &slot);
+    if (ret < 0) {
+      ALOGE(
+          "Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
+          producer_queue->id(), strerror(-ret));
+      return ErrorStatus(ENOMEM);
+    }
+    ALOGD_IF(
+        TRACE,
+        "Surface::CreateQueue: Allocated buffer at slot=%zu of capacity=%zu",
+        slot, capacity);
+  }
+
+  return {std::move(producer_queue)};
+}
+
+DisplayClient::DisplayClient(int* error)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+               DisplayProtocol::kClientPath),
+           kInfiniteTimeout) {
+  if (error)
+    *error = Client::error();
+}
+
+Status<Metrics> DisplayClient::GetDisplayMetrics() {
+  return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
+}
+
+Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
+    const SurfaceAttributes& attributes) {
+  int error;
+  if (auto client = Surface::Create(attributes, &error))
+    return {std::move(client)};
+  else
+    return ErrorStatus(error);
+}
+
+Status<std::unique_ptr<IonBuffer>> DisplayClient::GetNamedBuffer(
+    const std::string& name) {
+  auto status = InvokeRemoteMethod<DisplayProtocol::GetNamedBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "DisplayClient::GetNamedBuffer: Failed to get named buffer: name=%s; "
+        "error=%s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  auto ion_buffer = std::make_unique<IonBuffer>();
+  auto native_buffer_handle = status.take();
+  const int ret = native_buffer_handle.Import(ion_buffer.get());
+  if (ret < 0) {
+    ALOGE(
+        "DisplayClient::GetNamedBuffer: Failed to import named buffer: "
+        "name=%s; error=%s",
+        name.c_str(), strerror(-ret));
+    return ErrorStatus(-ret);
+  }
+
+  return {std::move(ion_buffer)};
+}
+
+Status<bool> DisplayClient::IsVrAppRunning() {
+  return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
+}
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
new file mode 100644
index 0000000..82dacf7
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -0,0 +1,78 @@
+#include "include/private/dvr/display_manager_client.h"
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_protocol.h>
+#include <utils/Log.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+namespace display {
+
+DisplayManagerClient::DisplayManagerClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayManagerProtocol::kClientPath)) {}
+
+DisplayManagerClient::~DisplayManagerClient() {}
+
+pdx::Status<std::vector<display::SurfaceState>>
+DisplayManagerClient::GetSurfaceState() {
+  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
+        status.GetErrorMessage().c_str());
+  }
+
+  return status;
+}
+
+pdx::Status<std::unique_ptr<IonBuffer>> DisplayManagerClient::SetupNamedBuffer(
+    const std::string& name, size_t size, uint64_t usage) {
+  auto status = InvokeRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>(
+      name, size, usage);
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::SetupPoseBuffer: Failed to create the named "
+        "buffer %s",
+        status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  auto ion_buffer = std::make_unique<IonBuffer>();
+  auto native_buffer_handle = status.take();
+  const int ret = native_buffer_handle.Import(ion_buffer.get());
+  if (ret < 0) {
+    ALOGE(
+        "DisplayClient::GetNamedBuffer: Failed to import named buffer: "
+        "name=%s; error=%s",
+        name.c_str(), strerror(-ret));
+    return ErrorStatus(-ret);
+  }
+
+  return {std::move(ion_buffer)};
+}
+
+pdx::Status<std::unique_ptr<ConsumerQueue>>
+DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
+  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
+      surface_id, queue_id);
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
+        "surface_id=%d queue_id=%d: %s",
+        surface_id, queue_id, status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  return {ConsumerQueue::Import(status.take())};
+}
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
new file mode 100644
index 0000000..773f9a5
--- /dev/null
+++ b/libs/vr/libdisplay/display_protocol.cpp
@@ -0,0 +1,13 @@
+#include "include/private/dvr/display_protocol.h"
+
+namespace android {
+namespace dvr {
+namespace display {
+
+constexpr char DisplayProtocol::kClientPath[];
+constexpr char DisplayManagerProtocol::kClientPath[];
+constexpr char VSyncProtocol::kClientPath[];
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/dvr/dvr_display_types.h b/libs/vr/libdisplay/include/dvr/dvr_display_types.h
new file mode 100644
index 0000000..25364d8
--- /dev/null
+++ b/libs/vr/libdisplay/include/dvr/dvr_display_types.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_DVR_DISPLAY_TYPES_H_
+#define ANDROID_DVR_DISPLAY_TYPES_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+// Define types used in pose buffer fields. These types have atomicity
+// guarantees that are useful in lock-free shared memory ring buffers.
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__((__vector_size__(16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+// VrFlinger display manager surface state snapshots per surface flags
+// indicating what changed since the last snapshot.
+enum {
+  // No changes.
+  DVR_SURFACE_UPDATE_FLAGS_NONE = 0,
+  // This surface is new.
+  DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE = (1 << 0),
+  // Buffer queues added/removed.
+  DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED = (1 << 1),
+  // Visibility/z-order changed.
+  DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED = (1 << 2),
+  // Generic attributes changed.
+  DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED = (1 << 3),
+};
+
+// Surface attribute keys. VrFlinger defines keys in the negative integer space.
+// The compositor is free to use keys in the positive integer space for
+// implementation-defined purposes.
+enum {
+  // DIRECT: bool
+  // Determines whether a direct surface is created (compositor output) or an
+  // application surface. Defaults to false (application surface). May only be
+  // set to true by a process with either UID=root or UID validated with
+  // IsTrustedUid() (VrCore).
+  DVR_SURFACE_ATTRIBUTE_DIRECT = -3,
+  // Z_ORDER: int32_t
+  // Interpreted by VrFlinger only on direct surfaces to order the corresponding
+  // hardware layers. More positive values render on top of more negative
+  // values.
+  DVR_SURFACE_ATTRIBUTE_Z_ORDER = -2,
+  // VISIBLE: bool
+  // Interpreted by VrFlinger only on direct surfaces to determine whether a
+  // surface is assigned to a hardware layer or ignored.
+  DVR_SURFACE_ATTRIBUTE_VISIBLE = -1,
+  // INVALID
+  // Invalid key. No attributes should have this key.
+  DVR_SURFACE_ATTRIBUTE_INVALID = 0,
+  // FIRST_USER_KEY
+  // VrFlinger ingores any keys with this value or greater, passing them to the
+  // compositor through surface state query results.
+  DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY = 1,
+};
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_DISPLAY_TYPES_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
new file mode 100644
index 0000000..7a7f670
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
+#define ANDROID_DVR_DISPLAY_CLIENT_H_
+
+#include <hardware/hwcomposer.h>
+#include <pdx/client.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_protocol.h>
+
+namespace android {
+namespace dvr {
+namespace display {
+
+class Surface : public pdx::ClientBase<Surface> {
+ public:
+  // Utility named constructor. This can be removed once ClientBase::Create is
+  // refactored to return Status<T> types.
+  static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
+      const SurfaceAttributes& attributes) {
+    int error;
+    pdx::Status<std::unique_ptr<Surface>> status;
+    if (auto surface = Create(attributes, &error))
+      status.SetValue(std::move(surface));
+    else
+      status.SetError(error);
+    return status;
+  }
+
+  int surface_id() const { return surface_id_; }
+  int z_order() const { return z_order_; }
+  bool visible() const { return visible_; }
+
+  pdx::Status<void> SetVisible(bool visible);
+  pdx::Status<void> SetZOrder(int z_order);
+  pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
+
+  // Creates an empty queue.
+  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue();
+
+  // Creates a queue and populates it with |capacity| buffers of the specified
+  // parameters.
+  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
+                                                          uint32_t height,
+                                                          uint32_t layer_count,
+                                                          uint32_t format,
+                                                          uint64_t usage,
+                                                          size_t capacity);
+
+ private:
+  friend BASE;
+
+  int surface_id_ = -1;
+  int z_order_ = 0;
+  bool visible_ = false;
+
+  // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
+  // returns Status<T>.
+  explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
+  explicit Surface(pdx::LocalChannelHandle channel_handle,
+                   int* error = nullptr);
+
+  Surface(const Surface&) = delete;
+  void operator=(const Surface&) = delete;
+};
+
+class DisplayClient : public pdx::ClientBase<DisplayClient> {
+ public:
+  pdx::Status<Metrics> GetDisplayMetrics();
+  pdx::Status<std::unique_ptr<IonBuffer>> GetNamedBuffer(
+      const std::string& name);
+  pdx::Status<std::unique_ptr<Surface>> CreateSurface(
+      const SurfaceAttributes& attributes);
+
+  // Temporary query for current VR status. Will be removed later.
+  pdx::Status<bool> IsVrAppRunning();
+
+ private:
+  friend BASE;
+
+  explicit DisplayClient(int* error = nullptr);
+
+  DisplayClient(const DisplayClient&) = delete;
+  void operator=(const DisplayClient&) = delete;
+};
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
new file mode 100644
index 0000000..fea8415
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
+#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include <pdx/client.h>
+#include <pdx/status.h>
+#include <private/dvr/display_protocol.h>
+
+namespace android {
+namespace dvr {
+
+class IonBuffer;
+class ConsumerQueue;
+
+namespace display {
+
+class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
+ public:
+  ~DisplayManagerClient() override;
+
+  pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
+  pdx::Status<std::unique_ptr<IonBuffer>> SetupNamedBuffer(
+      const std::string& name, size_t size, uint64_t usage);
+  pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
+                                                              int queue_id);
+
+  using Client::event_fd;
+
+  pdx::Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel())
+      return client_channel->GetEventMask(events);
+    else
+      return pdx::ErrorStatus(EINVAL);
+  }
+
+ private:
+  friend BASE;
+
+  DisplayManagerClient();
+
+  DisplayManagerClient(const DisplayManagerClient&) = delete;
+  void operator=(const DisplayManagerClient&) = delete;
+};
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
new file mode 100644
index 0000000..f34d61f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -0,0 +1,283 @@
+#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
+#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <map>
+
+#include <dvr/dvr_display_types.h>
+
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+// RPC protocol definitions for DVR display services (VrFlinger).
+
+namespace android {
+namespace dvr {
+namespace display {
+
+// Native display metrics.
+struct Metrics {
+  // Basic display properties.
+  uint32_t display_width;
+  uint32_t display_height;
+  uint32_t display_x_dpi;
+  uint32_t display_y_dpi;
+  uint32_t vsync_period_ns;
+
+  // HMD metrics.
+  // TODO(eieio): Determine how these fields should be populated. On phones
+  // these values are determined at runtime by VrCore based on which headset the
+  // phone is in. On dedicated hardware this needs to come from somewhere else.
+  // Perhaps these should be moved to a separate structure that is returned by a
+  // separate runtime call.
+  uint32_t distorted_width;
+  uint32_t distorted_height;
+  uint32_t hmd_ipd_mm;
+  float inter_lens_distance_m;
+  std::array<float, 4> left_fov_lrbt;
+  std::array<float, 4> right_fov_lrbt;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
+                           display_x_dpi, display_y_dpi, vsync_period_ns,
+                           distorted_width, distorted_height, hmd_ipd_mm,
+                           inter_lens_distance_m, left_fov_lrbt,
+                           right_fov_lrbt);
+};
+
+// Serializable base type for enum structs. Enum structs are easier to use than
+// enum classes, especially for bitmasks. This base type provides common
+// utilities for flags types.
+template <typename Integer>
+class Flags {
+ public:
+  using Base = Flags<Integer>;
+  using Type = Integer;
+
+  Flags(const Integer& value) : value_{value} {}
+  Flags(const Flags&) = default;
+  Flags& operator=(const Flags&) = default;
+
+  Integer value() const { return value_; }
+  operator Integer() const { return value_; }
+
+  bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
+  bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
+
+  void Set(Integer bits) { value_ |= bits; }
+  void Clear(Integer bits) { value_ &= ~bits; }
+
+  Integer operator|(Integer bits) const { return value_ | bits; }
+  Integer operator&(Integer bits) const { return value_ & bits; }
+
+  Flags& operator|=(Integer bits) {
+    value_ |= bits;
+    return *this;
+  }
+  Flags& operator&=(Integer bits) {
+    value_ &= bits;
+    return *this;
+  }
+
+ private:
+  Integer value_;
+
+  PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
+};
+
+// Flags indicating what changed since last update.
+struct SurfaceUpdateFlags : public Flags<uint32_t> {
+  enum : Type {
+    None = DVR_SURFACE_UPDATE_FLAGS_NONE,
+    NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
+    BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
+    VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
+    AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
+  };
+
+  SurfaceUpdateFlags() : Base{None} {}
+  using Base::Base;
+};
+
+// Surface attribute key/value types.
+using SurfaceAttributeKey = int32_t;
+using SurfaceAttributeValue =
+    pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
+                      std::array<float, 3>, std::array<float, 4>,
+                      std::array<float, 8>, std::array<float, 16>>;
+
+// Defined surface attribute keys.
+struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
+  enum : Type {
+    // Keys in the negative integer space are interpreted by VrFlinger for
+    // direct surfaces.
+    Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
+    ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+    Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
+
+    // Invalid key. May be used to terminate C style lists in public API code.
+    Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
+
+    // Positive keys are interpreted by the compositor only.
+    FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
+  };
+
+  SurfaceAttribute() : Base{Invalid} {}
+  using Base::Base;
+};
+
+// Collection of surface attribute key/value pairs.
+using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
+
+struct SurfaceState {
+  int32_t surface_id;
+  int32_t process_id;
+  int32_t user_id;
+
+  SurfaceAttributes surface_attributes;
+  SurfaceUpdateFlags update_flags;
+  std::vector<int32_t> queue_ids;
+
+  // Convenience accessors.
+  bool GetVisible() const {
+    bool bool_value = false;
+    GetAttribute(SurfaceAttribute::Visible, &bool_value,
+                 ValidTypes<int32_t, int64_t, bool, float>{});
+    return bool_value;
+  }
+
+  int GetZOrder() const {
+    int int_value = 0;
+    GetAttribute(SurfaceAttribute::ZOrder, &int_value,
+                 ValidTypes<int32_t, int64_t, float>{});
+    return int_value;
+  }
+
+ private:
+  template <typename... Types>
+  struct ValidTypes {};
+
+  template <typename ReturnType, typename... Types>
+  bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
+                    ValidTypes<Types...>) const {
+    auto search = surface_attributes.find(key);
+    if (search != surface_attributes.end())
+      return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
+    else
+      return false;
+  }
+
+  PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
+                           surface_attributes, update_flags, queue_ids);
+};
+
+struct SurfaceInfo {
+  int surface_id;
+  bool visible;
+  int z_order;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
+};
+
+struct DisplayProtocol {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/client";
+
+  // Op codes.
+  enum {
+    kOpGetMetrics = 0,
+    kOpGetNamedBuffer,
+    kOpIsVrAppRunning,
+    kOpCreateSurface,
+    kOpGetSurfaceInfo,
+    kOpCreateQueue,
+    kOpSetAttributes,
+  };
+
+  // Aliases.
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
+  PDX_REMOTE_METHOD(GetNamedBuffer, kOpGetNamedBuffer,
+                    LocalNativeBufferHandle(std::string name));
+  PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
+  PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
+                    SurfaceInfo(const SurfaceAttributes& attributes));
+  PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
+  PDX_REMOTE_METHOD(CreateQueue, kOpCreateQueue,
+                    LocalChannelHandle(size_t meta_size_bytes));
+  PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
+                    void(const SurfaceAttributes& attributes));
+};
+
+struct DisplayManagerProtocol {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/manager";
+
+  // Op codes.
+  enum {
+    kOpGetSurfaceState = 0,
+    kOpGetSurfaceQueue,
+    kOpSetupNamedBuffer,
+  };
+
+  // Aliases.
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
+                    std::vector<SurfaceState>(Void));
+  PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
+                    LocalChannelHandle(int surface_id, int queue_id));
+  PDX_REMOTE_METHOD(SetupNamedBuffer, kOpSetupNamedBuffer,
+                    LocalNativeBufferHandle(const std::string& name,
+                                            size_t size, uint64_t usage));
+};
+
+struct VSyncSchedInfo {
+  int64_t vsync_period_ns;
+  int64_t timestamp_ns;
+  uint32_t next_vsync_count;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
+                           next_vsync_count);
+};
+
+struct VSyncProtocol {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/vsync";
+
+  // Op codes.
+  enum {
+    kOpWait = 0,
+    kOpAck,
+    kOpGetLastTimestamp,
+    kOpGetSchedInfo,
+    kOpAcknowledge,
+  };
+
+  // Aliases.
+  using Void = pdx::rpc::Void;
+  using Timestamp = int64_t;
+
+  // Methods.
+  PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
+  PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
+  PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
+  PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
+};
+
+}  // namespace display
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
new file mode 100644
index 0000000..1eeb80e
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
@@ -0,0 +1,69 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
+#define ANDROID_DVR_VSYNC_CLIENT_H_
+
+#include <stdint.h>
+
+#include <pdx/client.h>
+
+struct dvr_vsync_client {};
+
+namespace android {
+namespace dvr {
+
+/*
+ * VSyncClient is a remote interface to the vsync service in displayd.
+ * This class is used to wait for and retrieve information about the
+ * display vsync.
+ */
+class VSyncClient : public pdx::ClientBase<VSyncClient>,
+                    public dvr_vsync_client {
+ public:
+  /*
+   * Wait for the next vsync signal.
+   * The timestamp (in ns) is written into *ts when ts is non-NULL.
+   */
+  int Wait(int64_t* timestamp_ns);
+
+  /*
+   * Returns the file descriptor used to communicate with the vsync system
+   * service or -1 on error.
+   */
+  int GetFd();
+
+  /*
+   * Clears the select/poll/epoll event so that subsequent calls to
+   * these will not signal until the next vsync.
+   */
+  int Acknowledge();
+
+  /*
+   * Get the timestamp of the last vsync event in ns. This call has
+   * the same side effect on events as Acknowledge(), which saves
+   * an IPC message.
+   */
+  int GetLastTimestamp(int64_t* timestamp_ns);
+
+  /*
+   * Get vsync scheduling info.
+   * Get the estimated timestamp of the next GPU lens warp preemption event in
+   * ns. Also returns the corresponding vsync count that the next lens warp
+   * operation will target. This call has the same side effect on events as
+   * Acknowledge(), which saves an IPC message.
+   */
+  int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
+                   uint32_t* next_vsync_count);
+
+ private:
+  friend BASE;
+
+  VSyncClient();
+  explicit VSyncClient(long timeout_ms);
+
+  VSyncClient(const VSyncClient&) = delete;
+  void operator=(const VSyncClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/system/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
new file mode 100644
index 0000000..bc6cf6c
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,76 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <log/log.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_protocol.h>
+
+using android::dvr::display::VSyncProtocol;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+VSyncClient::VSyncClient(long timeout_ms)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+               VSyncProtocol::kClientPath),
+           timeout_ms) {}
+
+VSyncClient::VSyncClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          VSyncProtocol::kClientPath)) {}
+
+int VSyncClient::Wait(int64_t* timestamp_ns) {
+  auto status = InvokeRemoteMethod<VSyncProtocol::Wait>();
+  if (!status) {
+    ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  if (timestamp_ns != nullptr) {
+    *timestamp_ns = status.get();
+  }
+  return 0;
+}
+
+int VSyncClient::GetFd() { return event_fd(); }
+
+int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
+  auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>();
+  if (!status) {
+    ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+  *timestamp_ns = status.get();
+  return 0;
+}
+
+int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
+                              uint32_t* next_vsync_count) {
+  if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
+    return -EINVAL;
+
+  auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>();
+  if (!status) {
+    ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *vsync_period_ns = status.get().vsync_period_ns;
+  *timestamp_ns = status.get().timestamp_ns;
+  *next_vsync_count = status.get().next_vsync_count;
+  return 0;
+}
+
+int VSyncClient::Acknowledge() {
+  auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>();
+  ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
new file mode 100644
index 0000000..fa78b1c
--- /dev/null
+++ b/libs/vr/libdvr/Android.bp
@@ -0,0 +1,89 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+cc_library_headers {
+    name: "libdvr_headers",
+    owner: "google",
+    export_include_dirs: ["include"],
+}
+
+cflags = [
+    "-DLOG_TAG=\"libdvr\"",
+]
+
+srcs = [
+    "dvr_api.cpp",
+    "dvr_buffer.cpp",
+    "dvr_buffer_queue.cpp",
+    "dvr_display_manager.cpp",
+    "dvr_hardware_composer_client.cpp",
+    "dvr_surface.cpp",
+    "dvr_vsync.cpp",
+]
+
+static_libs = [
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libdisplay",
+    "libvrsensor",
+    "libvirtualtouchpadclient",
+    "libvr_hwc-impl",
+    "libvr_hwc-binder",
+    "libgrallocusage",
+    "libpdx_default_transport",
+]
+
+shared_libs = [
+    "android.hardware.graphics.bufferqueue@1.0",
+    "android.hidl.token@1.0-utils",
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libcutils",
+    "libutils",
+    "libnativewindow",
+    "libgui",
+    "libui",
+]
+
+cc_library_shared {
+    name: "libdvr",
+    owner: "google",
+    cflags: cflags,
+    header_libs: ["libdvr_headers"],
+    export_header_lib_headers: ["libdvr_headers"],
+    srcs: srcs,
+    static_libs: static_libs,
+    shared_libs: shared_libs,
+    version_script: "exported_apis.lds",
+}
+
+// Also build a static libdvr for linking into tests. The linker script
+// restricting function access in the shared lib makes it inconvenient to use in
+// test code.
+cc_library_static {
+    name: "libdvr_static",
+    owner: "google",
+    cflags: cflags,
+    header_libs: ["libdvr_headers"],
+    export_header_lib_headers: ["libdvr_headers"],
+    srcs: srcs,
+    static_libs: static_libs,
+    shared_libs: shared_libs,
+}
+
+subdirs = [
+    "tests",
+]
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
new file mode 100644
index 0000000..2c95583
--- /dev/null
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -0,0 +1,47 @@
+#include "include/dvr/dvr_api.h"
+
+#include <errno.h>
+#include <utils/Log.h>
+
+// Headers from libdvr
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+#include <dvr/dvr_vsync.h>
+
+// Headers not yet moved into libdvr.
+// TODO(jwcai) Move these once their callers are moved into Google3.
+#include <dvr/dvr_hardware_composer_client.h>
+#include <dvr/pose_client.h>
+#include <dvr/virtual_touchpad_client.h>
+
+extern "C" {
+
+int dvrGetApi(void* api, size_t struct_size, int version) {
+  ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
+        version);
+  if (version == 1) {
+    if (struct_size != sizeof(DvrApi_v1)) {
+      ALOGE("dvrGetApi: Size mismatch: expected %zu; actual %zu",
+            sizeof(DvrApi_v1), struct_size);
+      return -EINVAL;
+    }
+    DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
+
+// Defines an API entry for V1 (no version suffix).
+#define DVR_V1_API_ENTRY(name) dvr_api->name = dvr##name
+
+#include "include/dvr/dvr_api_entries.h"
+
+// Undefine macro definitions to play nice with Google3 style rules.
+#undef DVR_V1_API_ENTRY
+
+    return 0;
+  }
+
+  ALOGE("dvrGetApi: Unknown API version=%d", version);
+  return -EINVAL;
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
new file mode 100644
index 0000000..82469b8
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -0,0 +1,202 @@
+#include "include/dvr/dvr_buffer.h"
+
+#include <android/hardware_buffer.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <ui/GraphicBuffer.h>
+
+#include "dvr_internal.h"
+
+using namespace android;
+
+namespace android {
+namespace dvr {
+
+DvrBuffer* CreateDvrBufferFromIonBuffer(
+    const std::shared_ptr<IonBuffer>& ion_buffer) {
+  if (!ion_buffer)
+    return nullptr;
+  return new DvrBuffer{std::move(ion_buffer)};
+}
+
+}  // namespace dvr
+}  // namespace android
+
+namespace {
+
+int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer,
+                             AHardwareBuffer** hardware_buffer) {
+  if (!hardware_buffer || !graphic_buffer) {
+    return -EINVAL;
+  }
+  *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer);
+  AHardwareBuffer_acquire(*hardware_buffer);
+  return 0;
+}
+
+}  // anonymous namespace
+
+extern "C" {
+
+void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) {
+  if (write_buffer)
+    *write_buffer = new DvrWriteBuffer;
+}
+
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
+  delete write_buffer;
+}
+
+int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
+  return write_buffer && write_buffer->write_buffer;
+}
+
+int dvrWriteBufferClear(DvrWriteBuffer* write_buffer) {
+  if (!write_buffer)
+    return -EINVAL;
+
+  write_buffer->write_buffer = nullptr;
+  return 0;
+}
+
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
+  if (!write_buffer || !write_buffer->write_buffer)
+    return -EINVAL;
+
+  return write_buffer->write_buffer->id();
+}
+
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+                                     AHardwareBuffer** hardware_buffer) {
+  if (!write_buffer || !write_buffer->write_buffer)
+    return -EINVAL;
+
+  return ConvertToAHardwareBuffer(
+      write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer);
+}
+
+int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
+                       const void* meta, size_t meta_size_bytes) {
+  if (!write_buffer || !write_buffer->write_buffer)
+    return -EINVAL;
+
+  pdx::LocalHandle fence(ready_fence_fd);
+  int result = write_buffer->write_buffer->Post(fence, meta, meta_size_bytes);
+  return result;
+}
+
+int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd) {
+  if (!write_buffer || !write_buffer->write_buffer || !release_fence_fd)
+    return -EINVAL;
+
+  pdx::LocalHandle release_fence;
+  int result = write_buffer->write_buffer->Gain(&release_fence);
+  *release_fence_fd = release_fence.Release();
+  return result;
+}
+
+int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer) {
+  if (!write_buffer || !write_buffer->write_buffer)
+    return -EINVAL;
+
+  return write_buffer->write_buffer->GainAsync();
+}
+
+void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) {
+  if (read_buffer)
+    *read_buffer = new DvrReadBuffer;
+}
+
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }
+
+int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
+  return read_buffer && read_buffer->read_buffer;
+}
+
+int dvrReadBufferClear(DvrReadBuffer* read_buffer) {
+  if (!read_buffer)
+    return -EINVAL;
+
+  read_buffer->read_buffer = nullptr;
+  return 0;
+}
+
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return -EINVAL;
+
+  return read_buffer->read_buffer->id();
+}
+
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+                                    AHardwareBuffer** hardware_buffer) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return -EINVAL;
+
+  return ConvertToAHardwareBuffer(
+      read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer);
+}
+
+int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
+                         void* meta, size_t meta_size_bytes) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return -EINVAL;
+
+  pdx::LocalHandle ready_fence;
+  int result =
+      read_buffer->read_buffer->Acquire(&ready_fence, meta, meta_size_bytes);
+  *ready_fence_fd = ready_fence.Release();
+  return result;
+}
+
+int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return -EINVAL;
+
+  pdx::LocalHandle fence(release_fence_fd);
+  int result = read_buffer->read_buffer->Release(fence);
+  return result;
+}
+
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return -EINVAL;
+
+  return read_buffer->read_buffer->ReleaseAsync();
+}
+
+void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }
+
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+                                AHardwareBuffer** hardware_buffer) {
+  if (!buffer || !buffer->buffer || !hardware_buffer) {
+    return -EINVAL;
+  }
+
+  return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
+                                  hardware_buffer);
+}
+
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+    DvrWriteBuffer* write_buffer) {
+  if (!write_buffer || !write_buffer->write_buffer)
+    return nullptr;
+
+  return write_buffer->write_buffer->native_handle();
+}
+
+const struct native_handle* dvrReadBufferGetNativeHandle(
+    DvrReadBuffer* read_buffer) {
+  if (!read_buffer || !read_buffer->read_buffer)
+    return nullptr;
+
+  return read_buffer->read_buffer->native_handle();
+}
+
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) {
+  if (!buffer || !buffer->buffer)
+    return nullptr;
+
+  return buffer->buffer->handle();
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
new file mode 100644
index 0000000..f668510
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -0,0 +1,204 @@
+#include "include/dvr/dvr_api.h"
+#include "include/dvr/dvr_buffer_queue.h"
+
+#include <android/native_window.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include "dvr_internal.h"
+
+#define CHECK_PARAM(param)                                               \
+  LOG_ALWAYS_FATAL_IF(param == nullptr, "%s: " #param "cannot be NULL.", \
+                      __FUNCTION__)
+
+using namespace android;
+
+namespace android {
+namespace dvr {
+
+DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue(
+    const std::shared_ptr<dvr::ProducerQueue>& producer_queue) {
+  return new DvrWriteBufferQueue{std::move(producer_queue)};
+}
+
+DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue(
+    const std::shared_ptr<dvr::ConsumerQueue>& consumer_queue) {
+  return new DvrReadBufferQueue{std::move(consumer_queue)};
+}
+
+dvr::ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue(
+    DvrWriteBufferQueue* write_queue) {
+  return write_queue->producer_queue.get();
+}
+
+}  // namespace dvr
+}  // namespace android
+
+extern "C" {
+
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
+  if (write_queue != nullptr && write_queue->native_window != nullptr)
+    ANativeWindow_release(write_queue->native_window);
+
+  delete write_queue;
+}
+
+ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
+  if (!write_queue || !write_queue->producer_queue)
+    return -EINVAL;
+
+  return write_queue->producer_queue->capacity();
+}
+
+int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
+  if (!write_queue)
+    return -EINVAL;
+
+  return write_queue->producer_queue->id();
+}
+
+int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
+                                          ANativeWindow** out_window) {
+  if (!write_queue || !out_window)
+    return -EINVAL;
+
+  if (write_queue->producer_queue->metadata_size() !=
+      sizeof(DvrNativeBufferMetadata)) {
+    ALOGE(
+        "The size of buffer metadata (%zu) of the write queue does not match "
+        "of size of DvrNativeBufferMetadata (%zu).",
+        write_queue->producer_queue->metadata_size(),
+        sizeof(DvrNativeBufferMetadata));
+    return -EINVAL;
+  }
+
+  // Lazy creation of |native_window|.
+  if (write_queue->native_window == nullptr) {
+    sp<IGraphicBufferProducer> gbp =
+        dvr::BufferHubQueueProducer::Create(write_queue->producer_queue);
+    sp<Surface> surface = new Surface(gbp, true);
+    write_queue->native_window = static_cast<ANativeWindow*>(surface.get());
+    ANativeWindow_acquire(write_queue->native_window);
+  }
+
+  *out_window = write_queue->native_window;
+  return 0;
+}
+
+int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
+                                       DvrReadBufferQueue** out_read_queue) {
+  if (!write_queue || !write_queue->producer_queue || !out_read_queue)
+    return -EINVAL;
+
+  auto read_queue = std::make_unique<DvrReadBufferQueue>();
+  read_queue->consumer_queue =
+      write_queue->producer_queue->CreateConsumerQueue();
+  if (read_queue->consumer_queue == nullptr) {
+    ALOGE(
+        "dvrWriteBufferQueueCreateReadQueue: Failed to create consumer queue "
+        "from DvrWriteBufferQueue[%p].",
+        write_queue);
+    return -ENOMEM;
+  }
+
+  *out_read_queue = read_queue.release();
+  return 0;
+}
+
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+                               DvrWriteBuffer* write_buffer,
+                               int* out_fence_fd) {
+  if (!write_queue || !write_queue->producer_queue || !write_buffer ||
+      !out_fence_fd) {
+    return -EINVAL;
+  }
+
+  size_t slot;
+  pdx::LocalHandle release_fence;
+  auto buffer_status =
+      write_queue->producer_queue->Dequeue(timeout, &slot, &release_fence);
+  if (!buffer_status) {
+    ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+             "dvrWriteBufferQueueDequeue: Failed to dequeue buffer: %s",
+             buffer_status.GetErrorMessage().c_str());
+    return -buffer_status.error();
+  }
+
+  write_buffer->write_buffer = buffer_status.take();
+  *out_fence_fd = release_fence.Release();
+  return 0;
+}
+
+// ReadBufferQueue
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
+  delete read_queue;
+}
+
+ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
+  if (!read_queue)
+    return -EINVAL;
+
+  return read_queue->consumer_queue->capacity();
+}
+
+int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
+  if (!read_queue)
+    return -EINVAL;
+
+  return read_queue->consumer_queue->id();
+}
+
+int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
+                                      DvrReadBufferQueue** out_read_queue) {
+  if (!read_queue || !read_queue->consumer_queue || !out_read_queue)
+    return -EINVAL;
+
+  auto new_read_queue = std::make_unique<DvrReadBufferQueue>();
+  new_read_queue->consumer_queue =
+      read_queue->consumer_queue->CreateConsumerQueue();
+  if (new_read_queue->consumer_queue == nullptr) {
+    ALOGE(
+        "dvrReadBufferQueueCreateReadQueue: Failed to create consumer queue "
+        "from DvrReadBufferQueue[%p].",
+        read_queue);
+    return -ENOMEM;
+  }
+
+  *out_read_queue = new_read_queue.release();
+  return 0;
+}
+
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+                              DvrReadBuffer* read_buffer, int* out_fence_fd,
+                              void* out_meta, size_t meta_size_bytes) {
+  if (!read_queue || !read_queue->consumer_queue || !read_buffer ||
+      !out_fence_fd || !out_meta) {
+    return -EINVAL;
+  }
+
+  if (meta_size_bytes != read_queue->consumer_queue->metadata_size()) {
+    ALOGE(
+        "dvrReadBufferQueueDequeue: Invalid metadata size, expected (%zu), "
+        "but actual (%zu).",
+        read_queue->consumer_queue->metadata_size(), meta_size_bytes);
+    return -EINVAL;
+  }
+
+  size_t slot;
+  pdx::LocalHandle acquire_fence;
+  auto buffer_status = read_queue->consumer_queue->Dequeue(
+      timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
+  if (!buffer_status) {
+    ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+             "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
+             buffer_status.GetErrorMessage().c_str());
+    return -buffer_status.error();
+  }
+
+  read_buffer->read_buffer = buffer_status.take();
+  *out_fence_fd = acquire_fence.Release();
+  return 0;
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
new file mode 100644
index 0000000..87636ec
--- /dev/null
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -0,0 +1,304 @@
+#include "include/dvr/dvr_display_manager.h"
+
+#include <dvr/dvr_buffer.h>
+#include <pdx/rpc/variant.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_manager_client.h>
+
+#include "dvr_internal.h"
+
+using android::AHardwareBuffer_convertToGrallocUsageBits;
+using android::dvr::BufferConsumer;
+using android::dvr::display::DisplayManagerClient;
+using android::dvr::display::SurfaceAttributes;
+using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceState;
+using android::dvr::CreateDvrReadBufferQueueFromConsumerQueue;
+using android::pdx::rpc::EmptyVariant;
+
+namespace {
+
+// Extracts type and value from the attribute Variant and writes them into the
+// respective fields of DvrSurfaceAttribute.
+struct AttributeVisitor {
+  DvrSurfaceAttribute* attribute;
+
+  void operator()(int32_t value) {
+    attribute->value.int32_value = value;
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
+  }
+  void operator()(int64_t value) {
+    attribute->value.int64_value = value;
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
+  }
+  void operator()(bool value) {
+    attribute->value.bool_value = value;
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
+  }
+  void operator()(float value) {
+    attribute->value.float_value = value;
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
+  }
+  void operator()(const std::array<float, 2>& value) {
+    std::copy(value.cbegin(), value.cend(), attribute->value.float2_value);
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
+  }
+  void operator()(const std::array<float, 3>& value) {
+    std::copy(value.cbegin(), value.cend(), attribute->value.float3_value);
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
+  }
+  void operator()(const std::array<float, 4>& value) {
+    std::copy(value.cbegin(), value.cend(), attribute->value.float4_value);
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
+  }
+  void operator()(const std::array<float, 8>& value) {
+    std::copy(value.cbegin(), value.cend(), attribute->value.float8_value);
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
+  }
+  void operator()(const std::array<float, 16>& value) {
+    std::copy(value.cbegin(), value.cend(), attribute->value.float16_value);
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
+  }
+  void operator()(EmptyVariant) {
+    attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
+  }
+};
+
+size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes,
+                                DvrSurfaceAttribute* attributes,
+                                size_t max_count) {
+  size_t count = 0;
+  for (const auto& attribute : surface_attributes) {
+    if (count >= max_count)
+      break;
+
+    // Copy the key and extract the Variant value using a visitor.
+    attributes[count].key = attribute.first;
+    attribute.second.Visit(AttributeVisitor{&attributes[count]});
+    count++;
+  }
+
+  return count;
+}
+
+}  // anonymous namespace
+
+extern "C" {
+
+struct DvrDisplayManager {
+  std::unique_ptr<DisplayManagerClient> client;
+};
+
+struct DvrSurfaceState {
+  std::vector<SurfaceState> state;
+};
+
+int dvrDisplayManagerCreate(DvrDisplayManager** client_out) {
+  if (!client_out)
+    return -EINVAL;
+
+  auto client = DisplayManagerClient::Create();
+  if (!client) {
+    ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!");
+    return -EIO;
+  }
+
+  *client_out = new DvrDisplayManager{std::move(client)};
+  return 0;
+}
+
+void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; }
+
+int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client,
+                                      const char* name, size_t size,
+                                      uint64_t usage, DvrBuffer** buffer_out) {
+  if (!client || !name || !buffer_out)
+    return -EINVAL;
+
+  uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+
+  auto buffer_status =
+      client->client->SetupNamedBuffer(name, size, gralloc_usage);
+  if (!buffer_status) {
+    ALOGE("dvrDisplayManagerSetupPoseBuffer: Failed to setup named buffer: %s",
+          buffer_status.GetErrorMessage().c_str());
+    return -buffer_status.error();
+  }
+
+  *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take());
+  return 0;
+}
+
+int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) {
+  if (!client)
+    return -EINVAL;
+
+  return client->client->event_fd();
+}
+
+int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
+                                             int in_events, int* out_events) {
+  if (!client || !out_events)
+    return -EINVAL;
+
+  auto status = client->client->GetEventMask(in_events);
+  if (!status)
+    return -status.error();
+
+  *out_events = status.get();
+  return 0;
+}
+
+int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
+                                     DvrSurfaceState* state) {
+  if (!client || !state)
+    return -EINVAL;
+
+  auto status = client->client->GetSurfaceState();
+  if (!status)
+    return -status.error();
+
+  state->state = status.take();
+  return 0;
+}
+
+int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
+                                        int surface_id, int queue_id,
+                                        DvrReadBufferQueue** queue_out) {
+  if (!client || !queue_out)
+    return -EINVAL;
+
+  auto status = client->client->GetSurfaceQueue(surface_id, queue_id);
+  if (!status) {
+    ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *queue_out = CreateDvrReadBufferQueueFromConsumerQueue(status.take());
+  return 0;
+}
+
+int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) {
+  if (!surface_state_out)
+    return -EINVAL;
+
+  *surface_state_out = new DvrSurfaceState{};
+  return 0;
+}
+
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) {
+  delete surface_state;
+}
+
+int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
+                                   size_t* count_out) {
+  if (!surface_state)
+    return -EINVAL;
+
+  *count_out = surface_state->state.size();
+  return 0;
+}
+
+int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
+                                  size_t surface_index,
+                                  DvrSurfaceUpdateFlags* flags_out) {
+  if (!surface_state || surface_index >= surface_state->state.size())
+    return -EINVAL;
+
+  *flags_out = surface_state->state[surface_index].update_flags;
+  return 0;
+}
+
+int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
+                                size_t surface_index, int* surface_id_out) {
+  if (!surface_state || surface_index >= surface_state->state.size())
+    return -EINVAL;
+
+  *surface_id_out = surface_state->state[surface_index].surface_id;
+  return 0;
+}
+
+int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
+                                size_t surface_index, int* process_id_out) {
+  if (!surface_state || surface_index >= surface_state->state.size())
+    return -EINVAL;
+
+  *process_id_out = surface_state->state[surface_index].process_id;
+  return 0;
+}
+
+int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
+                                 size_t surface_index, size_t* count_out) {
+  if (!surface_state || surface_index >= surface_state->state.size())
+    return -EINVAL;
+
+  *count_out = surface_state->state[surface_index].queue_ids.size();
+  return 0;
+}
+
+ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
+                                   size_t surface_index, int* queue_ids,
+                                   size_t max_count) {
+  if (!surface_state || surface_index >= surface_state->state.size())
+    return -EINVAL;
+
+  size_t i;
+  const auto& state = surface_state->state[surface_index];
+  for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) {
+    queue_ids[i] = state.queue_ids[i];
+  }
+
+  return i;
+}
+
+int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
+                             size_t surface_index, int* z_order_out) {
+  if (!surface_state || surface_index >= surface_state->state.size() ||
+      !z_order_out) {
+    return -EINVAL;
+  }
+
+  *z_order_out = surface_state->state[surface_index].GetZOrder();
+  return 0;
+}
+
+int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
+                              size_t surface_index, bool* visible_out) {
+  if (!surface_state || surface_index >= surface_state->state.size() ||
+      !visible_out) {
+    return -EINVAL;
+  }
+
+  *visible_out = surface_state->state[surface_index].GetVisible();
+  return 0;
+}
+
+int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
+                                     size_t surface_index, size_t* count_out) {
+  if (!surface_state || surface_index >= surface_state->state.size() ||
+      !count_out) {
+    return -EINVAL;
+  }
+
+  *count_out = surface_state->state[surface_index].surface_attributes.size();
+  return 0;
+}
+
+ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
+                                     size_t surface_index,
+                                     DvrSurfaceAttribute* attributes,
+                                     size_t max_count) {
+  if (!surface_state || surface_index >= surface_state->state.size() ||
+      !attributes) {
+    return -EINVAL;
+  }
+
+  return ConvertSurfaceAttributes(
+      surface_state->state[surface_index].surface_attributes, attributes,
+      max_count);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
new file mode 100644
index 0000000..d3ae299
--- /dev/null
+++ b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
@@ -0,0 +1,237 @@
+#include "include/dvr/dvr_hardware_composer_client.h"
+
+#include <android/dvr/IVrComposer.h>
+#include <android/dvr/BnVrComposerCallback.h>
+#include <android/hardware_buffer.h>
+#include <binder/IServiceManager.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <memory>
+
+struct DvrHwcFrame {
+  android::dvr::ComposerView::Frame frame;
+};
+
+namespace {
+
+class HwcCallback : public android::dvr::BnVrComposerCallback {
+ public:
+  explicit HwcCallback(DvrHwcOnFrameCallback callback,
+                       void* client_state);
+  ~HwcCallback() override;
+
+  std::unique_ptr<DvrHwcFrame> DequeueFrame();
+
+ private:
+  // android::dvr::BnVrComposerCallback:
+  android::binder::Status onNewFrame(
+      const android::dvr::ParcelableComposerFrame& frame,
+      android::dvr::ParcelableUniqueFd* fence) override;
+
+  DvrHwcOnFrameCallback callback_;
+  void* client_state_;
+
+  HwcCallback(const HwcCallback&) = delete;
+  void operator=(const HwcCallback&) = delete;
+};
+
+HwcCallback::HwcCallback(DvrHwcOnFrameCallback callback, void* client_state)
+    : callback_(callback), client_state_(client_state) {}
+
+HwcCallback::~HwcCallback() {}
+
+android::binder::Status HwcCallback::onNewFrame(
+    const android::dvr::ParcelableComposerFrame& frame,
+    android::dvr::ParcelableUniqueFd* fence) {
+  std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame());
+  dvr_frame->frame = frame.frame();
+
+  fence->set_fence(android::base::unique_fd(callback_(client_state_,
+                                                      dvr_frame.release())));
+  return android::binder::Status::ok();
+}
+
+}  // namespace
+
+struct DvrHwcClient {
+  android::sp<android::dvr::IVrComposer> composer;
+  android::sp<HwcCallback> callback;
+};
+
+DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) {
+  std::unique_ptr<DvrHwcClient> client(new DvrHwcClient());
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  client->composer = android::interface_cast<android::dvr::IVrComposer>(
+      sm->getService(android::dvr::IVrComposer::SERVICE_NAME()));
+  if (!client->composer.get())
+    return nullptr;
+
+  client->callback = new HwcCallback(callback, data);
+  android::binder::Status status = client->composer->registerObserver(
+      client->callback);
+  if (!status.isOk())
+    return nullptr;
+
+  return client.release();
+}
+
+void dvrHwcClientDestroy(DvrHwcClient* client) {
+  delete client;
+}
+
+void dvrHwcFrameDestroy(DvrHwcFrame* frame) {
+  delete frame;
+}
+
+DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) {
+  return frame->frame.display_id;
+}
+
+int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) {
+  return frame->frame.display_width;
+}
+
+int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) {
+  return frame->frame.display_height;
+}
+
+bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) {
+  return frame->frame.removed;
+}
+
+size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) {
+  return frame->frame.layers.size();
+}
+
+uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) {
+  return static_cast<uint32_t>(frame->frame.active_config);
+}
+
+uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) {
+  return static_cast<uint32_t>(frame->frame.color_mode);
+}
+
+void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
+                                  int32_t* out_hint) {
+  *out_hint = frame->frame.color_transform_hint;
+  memcpy(out_matrix, frame->frame.color_transform,
+         sizeof(frame->frame.color_transform));
+}
+
+uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) {
+  return static_cast<uint32_t>(frame->frame.power_mode);
+}
+
+uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) {
+  return static_cast<uint32_t>(frame->frame.vsync_enabled);
+}
+
+DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].id;
+}
+
+AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
+                                           size_t layer_index) {
+  AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer(
+      frame->frame.layers[layer_index].buffer.get());
+  AHardwareBuffer_acquire(buffer);
+  return buffer;
+}
+
+int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].fence->dup();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
+                                            size_t layer_index) {
+  return DvrHwcRecti{
+    frame->frame.layers[layer_index].display_frame.left,
+    frame->frame.layers[layer_index].display_frame.top,
+    frame->frame.layers[layer_index].display_frame.right,
+    frame->frame.layers[layer_index].display_frame.bottom,
+  };
+}
+
+DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) {
+  return DvrHwcRectf{
+    frame->frame.layers[layer_index].crop.left,
+    frame->frame.layers[layer_index].crop.top,
+    frame->frame.layers[layer_index].crop.right,
+    frame->frame.layers[layer_index].crop.bottom,
+  };
+}
+
+DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
+                                             size_t layer_index) {
+  return static_cast<DvrHwcBlendMode>(
+      frame->frame.layers[layer_index].blend_mode);
+}
+
+float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].alpha;
+}
+
+uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].type;
+}
+
+uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
+                                          size_t layer_index) {
+  return frame->frame.layers[layer_index].app_id;
+}
+
+uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].z_order;
+}
+
+void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
+                               int32_t* out_x, int32_t* out_y) {
+  *out_x = frame->frame.layers[layer_index].cursor_x;
+  *out_y = frame->frame.layers[layer_index].cursor_y;
+}
+
+uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].transform;
+}
+
+uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) {
+  return frame->frame.layers[layer_index].dataspace;
+}
+
+uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) {
+  const auto& color = frame->frame.layers[layer_index].color;
+  return color.r | (static_cast<uint32_t>(color.g) << 8) |
+         (static_cast<uint32_t>(color.b) << 16) |
+         (static_cast<uint32_t>(color.a) << 24);
+}
+
+uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
+                                              size_t layer_index) {
+  return frame->frame.layers[layer_index].visible_regions.size();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
+                                             size_t layer_index, size_t index) {
+  return DvrHwcRecti{
+      frame->frame.layers[layer_index].visible_regions[index].left,
+      frame->frame.layers[layer_index].visible_regions[index].top,
+      frame->frame.layers[layer_index].visible_regions[index].right,
+      frame->frame.layers[layer_index].visible_regions[index].bottom,
+  };
+}
+
+uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
+                                              size_t layer_index) {
+  return frame->frame.layers[layer_index].damaged_regions.size();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
+                                             size_t layer_index, size_t index) {
+  return DvrHwcRecti{
+      frame->frame.layers[layer_index].damaged_regions[index].left,
+      frame->frame.layers[layer_index].damaged_regions[index].top,
+      frame->frame.layers[layer_index].damaged_regions[index].right,
+      frame->frame.layers[layer_index].damaged_regions[index].bottom,
+  };
+}
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
new file mode 100644
index 0000000..89bef09
--- /dev/null
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -0,0 +1,70 @@
+#ifndef ANDROID_DVR_INTERNAL_H_
+#define ANDROID_DVR_INTERNAL_H_
+
+#include <sys/cdefs.h>
+
+#include <memory>
+
+extern "C" {
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+}  // extern "C"
+
+namespace android {
+namespace dvr {
+
+class BufferProducer;
+class BufferConsumer;
+class ConsumerQueue;
+class IonBuffer;
+class ProducerQueue;
+
+DvrBuffer* CreateDvrBufferFromIonBuffer(
+    const std::shared_ptr<IonBuffer>& ion_buffer);
+
+DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
+    const std::shared_ptr<BufferConsumer>& buffer_consumer);
+DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
+    const std::shared_ptr<BufferProducer>& buffer_producer);
+
+DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue(
+    const std::shared_ptr<ConsumerQueue>& consumer_queue);
+DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue(
+    const std::shared_ptr<ProducerQueue>& producer_queue);
+ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue(
+    DvrWriteBufferQueue* write_queue);
+
+}  // namespace dvr
+}  // namespace android
+
+extern "C" {
+
+struct DvrWriteBuffer {
+  std::shared_ptr<android::dvr::BufferProducer> write_buffer;
+};
+
+struct DvrReadBuffer {
+  std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
+};
+
+struct DvrBuffer {
+  std::shared_ptr<android::dvr::IonBuffer> buffer;
+};
+
+struct DvrWriteBufferQueue {
+  std::shared_ptr<android::dvr::ProducerQueue> producer_queue;
+  ANativeWindow* native_window{nullptr};
+};
+
+struct DvrReadBufferQueue {
+  std::shared_ptr<android::dvr::ConsumerQueue> consumer_queue;
+};
+
+}  // extern "C"
+
+#endif  // ANDROID_DVR_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
new file mode 100644
index 0000000..67e2ae8
--- /dev/null
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -0,0 +1,182 @@
+#include "include/dvr/dvr_surface.h"
+
+#include <inttypes.h>
+
+#include <private/dvr/display_client.h>
+
+#include "dvr_internal.h"
+
+using android::dvr::display::DisplayClient;
+using android::dvr::display::Surface;
+using android::dvr::display::SurfaceAttributes;
+using android::dvr::display::SurfaceAttributeValue;
+using android::dvr::CreateDvrReadBufferFromBufferConsumer;
+using android::dvr::CreateDvrWriteBufferQueueFromProducerQueue;
+
+namespace {
+
+bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes,
+                              size_t attribute_count,
+                              SurfaceAttributes* surface_attributes,
+                              size_t* error_index) {
+  for (size_t i = 0; i < attribute_count; i++) {
+    SurfaceAttributeValue value;
+    switch (attributes[i].value.type) {
+      case DVR_SURFACE_ATTRIBUTE_TYPE_INT32:
+        value = attributes[i].value.int32_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_INT64:
+        value = attributes[i].value.int64_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL:
+        value = attributes[i].value.bool_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT:
+        value = attributes[i].value.float_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2:
+        value = attributes[i].value.float2_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3:
+        value = attributes[i].value.float3_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4:
+        value = attributes[i].value.float4_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8:
+        value = attributes[i].value.float8_value;
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16:
+        value = attributes[i].value.float16_value;
+        break;
+      default:
+        *error_index = i;
+        return false;
+    }
+
+    surface_attributes->emplace(attributes[i].key, value);
+  }
+
+  return true;
+}
+
+}  // anonymous namespace
+
+extern "C" {
+
+struct DvrSurface {
+  std::unique_ptr<Surface> surface;
+};
+
+int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
+                     size_t attribute_count, DvrSurface** out_surface) {
+  if (out_surface == nullptr) {
+    ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface);
+    return -EINVAL;
+  }
+
+  size_t error_index;
+  SurfaceAttributes surface_attributes;
+  if (!ConvertSurfaceAttributes(attributes, attribute_count,
+                                &surface_attributes, &error_index)) {
+    ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64,
+          attributes[error_index].value.type);
+    return -EINVAL;
+  }
+
+  auto status = Surface::CreateSurface(surface_attributes);
+  if (!status) {
+    ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *out_surface = new DvrSurface{status.take()};
+  return 0;
+}
+
+void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; }
+
+int dvrSurfaceGetId(DvrSurface* surface) {
+  return surface->surface->surface_id();
+}
+
+int dvrSurfaceSetAttributes(DvrSurface* surface,
+                            const DvrSurfaceAttribute* attributes,
+                            size_t attribute_count) {
+  if (surface == nullptr || attributes == nullptr) {
+    ALOGE(
+        "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p "
+        "attribute_count=%zu",
+        surface, attributes, attribute_count);
+    return -EINVAL;
+  }
+
+  size_t error_index;
+  SurfaceAttributes surface_attributes;
+  if (!ConvertSurfaceAttributes(attributes, attribute_count,
+                                &surface_attributes, &error_index)) {
+    ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64,
+          attributes[error_index].value.type);
+    return -EINVAL;
+  }
+
+  auto status = surface->surface->SetAttributes(surface_attributes);
+  if (!status) {
+    ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  return 0;
+}
+
+int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
+                                     uint32_t height, uint32_t format,
+                                     uint32_t layer_count, uint64_t usage,
+                                     size_t capacity,
+                                     DvrWriteBufferQueue** out_writer) {
+  if (surface == nullptr || out_writer == nullptr) {
+    ALOGE(
+        "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, "
+        "out_writer=%p.",
+        surface, out_writer);
+    return -EINVAL;
+  }
+
+  auto status = surface->surface->CreateQueue(width, height, layer_count,
+                                              format, usage, capacity);
+  if (!status) {
+    ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *out_writer = CreateDvrWriteBufferQueueFromProducerQueue(status.take());
+  return 0;
+}
+
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer) {
+  auto client = DisplayClient::Create();
+  if (!client) {
+    ALOGE("dvrGetNamedBuffer: Failed to create display client!");
+    return -ECOMM;
+  }
+
+  if (out_buffer == nullptr || name == nullptr) {
+    ALOGE("dvrGetNamedBuffer: Invalid inputs: name=%p, out_buffer=%p.", name,
+          out_buffer);
+    return -EINVAL;
+  }
+
+  auto status = client->GetNamedBuffer(name);
+  if (!status) {
+    ALOGE("dvrGetNamedBuffer: Failed to find named buffer name=%s: %s", name,
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+  *out_buffer = CreateDvrBufferFromIonBuffer(status.take());
+  return 0;
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp
new file mode 100644
index 0000000..099240e
--- /dev/null
+++ b/libs/vr/libdvr/dvr_vsync.cpp
@@ -0,0 +1,33 @@
+#include "include/dvr/dvr_vsync.h"
+
+#include <utils/Log.h>
+
+#include <private/dvr/vsync_client.h>
+
+extern "C" {
+
+struct DvrVSyncClient {
+  std::unique_ptr<android::dvr::VSyncClient> client;
+};
+
+int dvrVSyncClientCreate(DvrVSyncClient** client_out) {
+  auto client = android::dvr::VSyncClient::Create();
+  if (!client) {
+    ALOGE("dvrVSyncClientCreate: Failed to create vsync client!");
+    return -EIO;
+  }
+
+  *client_out = new DvrVSyncClient{std::move(client)};
+  return 0;
+}
+
+void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; }
+
+int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
+                               int64_t* next_timestamp_ns,
+                               uint32_t* next_vsync_count) {
+  return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns,
+                                      next_vsync_count);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds
new file mode 100644
index 0000000..5ecb498
--- /dev/null
+++ b/libs/vr/libdvr/exported_apis.lds
@@ -0,0 +1,9 @@
+{
+  global:
+    # Whitelist the function to load the dvr api.
+    dvrGetApi;
+
+  local:
+    # Hide everything else.
+    *;
+};
diff --git a/libs/vr/libdvr/include/CPPLINT.cfg b/libs/vr/libdvr/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdvr/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
new file mode 100644
index 0000000..8a203e0
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -0,0 +1,312 @@
+#ifndef ANDROID_DVR_API_H_
+#define ANDROID_DVR_API_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <dvr/dvr_hardware_composer_defs.h>
+
+__BEGIN_DECLS
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrPoseAsync DvrPoseAsync;
+
+typedef uint64_t DvrSurfaceUpdateFlags;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrPose DvrPose;
+typedef struct DvrVSyncClient DvrVSyncClient;
+typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+typedef struct DvrSurface DvrSurface;
+typedef uint64_t DvrSurfaceAttributeType;
+typedef int32_t DvrSurfaceAttributeKey;
+
+typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue;
+typedef struct DvrSurfaceAttribute DvrSurfaceAttribute;
+
+struct native_handle;
+
+// dvr_display_manager.h
+typedef int (*DvrDisplayManagerCreatePtr)(DvrDisplayManager** client_out);
+typedef void (*DvrDisplayManagerDestroyPtr)(DvrDisplayManager* client);
+typedef int (*DvrDisplayManagerSetupNamedBufferPtr)(DvrDisplayManager* client,
+                                                    const char* name,
+                                                    size_t size, uint64_t usage,
+                                                    DvrBuffer** buffer_out);
+typedef int (*DvrDisplayManagerGetEventFdPtr)(DvrDisplayManager* client);
+typedef int (*DvrDisplayManagerTranslateEpollEventMaskPtr)(
+    DvrDisplayManager* client, int in_events, int* out_events);
+typedef int (*DvrDisplayManagerGetSurfaceStatePtr)(
+    DvrDisplayManager* client, DvrSurfaceState* surface_state);
+typedef int (*DvrDisplayManagerGetReadBufferQueuePtr)(
+    DvrDisplayManager* client, int surface_id, int queue_id,
+    DvrReadBufferQueue** queue_out);
+typedef int (*DvrSurfaceStateCreatePtr)(DvrSurfaceState** surface_state);
+typedef void (*DvrSurfaceStateDestroyPtr)(DvrSurfaceState* surface_state);
+typedef int (*DvrSurfaceStateGetSurfaceCountPtr)(DvrSurfaceState* surface_state,
+                                                 size_t* count_out);
+typedef int (*DvrSurfaceStateGetUpdateFlagsPtr)(
+    DvrSurfaceState* surface_state, size_t surface_index,
+    DvrSurfaceUpdateFlags* flags_out);
+typedef int (*DvrSurfaceStateGetSurfaceIdPtr)(DvrSurfaceState* surface_state,
+                                              size_t surface_index,
+                                              int* surface_id_out);
+typedef int (*DvrSurfaceStateGetProcessIdPtr)(DvrSurfaceState* surface_state,
+                                              size_t surface_index,
+                                              int* process_id_out);
+typedef int (*DvrSurfaceStateGetQueueCountPtr)(DvrSurfaceState* surface_state,
+                                               size_t surface_index,
+                                               size_t* count_out);
+typedef ssize_t (*DvrSurfaceStateGetQueueIdsPtr)(DvrSurfaceState* surface_state,
+                                                 size_t surface_index,
+                                                 int* queue_ids,
+                                                 size_t max_count);
+typedef int (*DvrSurfaceStateGetZOrderPtr)(DvrSurfaceState* surface_state,
+                                           size_t surface_index,
+                                           int* z_order_out);
+typedef int (*DvrSurfaceStateGetVisiblePtr)(DvrSurfaceState* surface_state,
+                                            size_t surface_index,
+                                            bool* visible_out);
+typedef int (*DvrSurfaceStateGetAttributeCountPtr)(
+    DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out);
+typedef ssize_t (*DvrSurfaceStateGetAttributesPtr)(
+    DvrSurfaceState* surface_state, size_t surface_index,
+    DvrSurfaceAttribute* attributes, size_t max_attribute_count);
+
+// dvr_buffer.h
+typedef void (*DvrWriteBufferCreateEmptyPtr)(DvrWriteBuffer** write_buffer_out);
+typedef void (*DvrWriteBufferDestroyPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferIsValidPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferClearPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferGetIdPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferGetAHardwareBufferPtr)(
+    DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrWriteBufferPostPtr)(DvrWriteBuffer* write_buffer,
+                                     int ready_fence_fd, const void* meta,
+                                     size_t meta_size_bytes);
+typedef int (*DvrWriteBufferGainPtr)(DvrWriteBuffer* write_buffer,
+                                     int* release_fence_fd);
+typedef int (*DvrWriteBufferGainAsyncPtr)(DvrWriteBuffer* write_buffer);
+typedef const struct native_handle* (*DvrWriteBufferGetNativeHandlePtr)(
+    DvrWriteBuffer* write_buffer);
+
+typedef void (*DvrReadBufferCreateEmptyPtr)(DvrReadBuffer** read_buffer_out);
+typedef void (*DvrReadBufferDestroyPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferIsValidPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferClearPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferGetIdPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferGetAHardwareBufferPtr)(
+    DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrReadBufferAcquirePtr)(DvrReadBuffer* read_buffer,
+                                       int* ready_fence_fd, void* meta,
+                                       size_t meta_size_bytes);
+typedef int (*DvrReadBufferReleasePtr)(DvrReadBuffer* read_buffer,
+                                       int release_fence_fd);
+typedef int (*DvrReadBufferReleaseAsyncPtr)(DvrReadBuffer* read_buffer);
+typedef const struct native_handle* (*DvrReadBufferGetNativeHandlePtr)(
+    DvrReadBuffer* read_buffer);
+
+typedef void (*DvrBufferDestroyPtr)(DvrBuffer* buffer);
+typedef int (*DvrBufferGetAHardwareBufferPtr)(
+    DvrBuffer* buffer, AHardwareBuffer** hardware_buffer);
+typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)(
+    DvrBuffer* buffer);
+
+// dvr_buffer_queue.h
+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 (*DvrWriteBufferQueueCreateReadQueuePtr)(
+    DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue);
+typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue,
+                                             int timeout,
+                                             DvrWriteBuffer* out_buffer,
+                                             int* out_fence_fd);
+typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue);
+typedef ssize_t (*DvrReadBufferQueueGetCapacityPtr)(
+    DvrReadBufferQueue* read_queue);
+typedef int (*DvrReadBufferQueueGetIdPtr)(DvrReadBufferQueue* read_queue);
+typedef int (*DvrReadBufferQueueCreateReadQueuePtr)(
+    DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue);
+typedef int (*DvrReadBufferQueueDequeuePtr)(DvrReadBufferQueue* read_queue,
+                                            int timeout,
+                                            DvrReadBuffer* out_buffer,
+                                            int* out_fence_fd, void* out_meta,
+                                            size_t meta_size_bytes);
+
+// dvr_surface.h
+typedef int (*DvrGetNamedBufferPtr)(const char* name, DvrBuffer** out_buffer);
+typedef int (*DvrSurfaceCreatePtr)(const DvrSurfaceAttribute* attributes,
+                                   size_t attribute_count,
+                                   DvrSurface** surface_out);
+typedef void (*DvrSurfaceDestroyPtr)(DvrSurface* surface);
+typedef int (*DvrSurfaceGetIdPtr)(DvrSurface* surface);
+typedef int (*DvrSurfaceSetAttributesPtr)(DvrSurface* surface,
+                                          const DvrSurfaceAttribute* attributes,
+                                          size_t attribute_count);
+typedef int (*DvrSurfaceCreateWriteBufferQueuePtr)(
+    DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format,
+    uint32_t layer_count, uint64_t usage, size_t capacity,
+    DvrWriteBufferQueue** queue_out);
+
+// vsync_client_api.h
+typedef int (*DvrVSyncClientCreatePtr)(DvrVSyncClient** client_out);
+typedef void (*DvrVSyncClientDestroyPtr)(DvrVSyncClient* client);
+typedef int (*DvrVSyncClientGetSchedInfoPtr)(DvrVSyncClient* client,
+                                             int64_t* vsync_period_ns,
+                                             int64_t* next_timestamp_ns,
+                                             uint32_t* next_vsync_count);
+
+// pose_client.h
+typedef DvrPose* (*DvrPoseCreatePtr)(void);
+typedef void (*DvrPoseDestroyPtr)(DvrPose* client);
+typedef int (*DvrPoseGetPtr)(DvrPose* client, uint32_t vsync_count,
+                             DvrPoseAsync* out_pose);
+typedef uint32_t (*DvrPoseGetVsyncCountPtr)(DvrPose* client);
+typedef int (*DvrPoseGetControllerPtr)(DvrPose* client, int32_t controller_id,
+                                       uint32_t vsync_count,
+                                       DvrPoseAsync* out_pose);
+
+// virtual_touchpad_client.h
+typedef DvrVirtualTouchpad* (*DvrVirtualTouchpadCreatePtr)(void);
+typedef void (*DvrVirtualTouchpadDestroyPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadAttachPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadDetachPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadTouchPtr)(DvrVirtualTouchpad* client,
+                                          int touchpad, float x, float y,
+                                          float pressure);
+typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client,
+                                                int touchpad, int buttons);
+
+// dvr_hardware_composer_client.h
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+typedef int (*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame);
+typedef DvrHwcClient* (*DvrHwcClientCreatePtr)(DvrHwcOnFrameCallback callback,
+                                               void* client_state);
+typedef void (*DvrHwcClientDestroyPtr)(DvrHwcClient* client);
+typedef void (*DvrHwcFrameDestroyPtr)(DvrHwcFrame* frame);
+typedef DvrHwcDisplay (*DvrHwcFrameGetDisplayIdPtr)(DvrHwcFrame* frame);
+typedef int32_t (*DvrHwcFrameGetDisplayWidthPtr)(DvrHwcFrame* frame);
+typedef int32_t (*DvrHwcFrameGetDisplayHeightPtr)(DvrHwcFrame* frame);
+typedef bool (*DvrHwcFrameGetDisplayRemovedPtr)(DvrHwcFrame* frame);
+typedef size_t (*DvrHwcFrameGetLayerCountPtr)(DvrHwcFrame* frame);
+typedef DvrHwcLayer (*DvrHwcFrameGetLayerIdPtr)(DvrHwcFrame* frame,
+                                                size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetActiveConfigPtr)(DvrHwcFrame* frame);
+typedef uint32_t (*DvrHwcFrameGetColorModePtr)(DvrHwcFrame* frame);
+typedef void (*DvrHwcFrameGetColorTransformPtr)(DvrHwcFrame* frame,
+                                                float* out_matrix,
+                                                int32_t* out_hint);
+typedef uint32_t (*DvrHwcFrameGetPowerModePtr)(DvrHwcFrame* frame);
+typedef uint32_t (*DvrHwcFrameGetVsyncEnabledPtr)(DvrHwcFrame* frame);
+typedef AHardwareBuffer* (*DvrHwcFrameGetLayerBufferPtr)(DvrHwcFrame* frame,
+                                                         size_t layer_index);
+typedef int (*DvrHwcFrameGetLayerFencePtr)(DvrHwcFrame* frame,
+                                           size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerDisplayFramePtr)(DvrHwcFrame* frame,
+                                                          size_t layer_index);
+typedef DvrHwcRectf (*DvrHwcFrameGetLayerCropPtr)(DvrHwcFrame* frame,
+                                                  size_t layer_index);
+typedef DvrHwcBlendMode (*DvrHwcFrameGetLayerBlendModePtr)(DvrHwcFrame* frame,
+                                                           size_t layer_index);
+typedef float (*DvrHwcFrameGetLayerAlphaPtr)(DvrHwcFrame* frame,
+                                             size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerTypePtr)(DvrHwcFrame* frame,
+                                               size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerApplicationIdPtr)(DvrHwcFrame* frame,
+                                                        size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerZOrderPtr)(DvrHwcFrame* frame,
+                                                 size_t layer_index);
+
+typedef void (*DvrHwcFrameGetLayerCursorPtr)(DvrHwcFrame* frame,
+                                             size_t layer_index, int32_t* out_x,
+                                             int32_t* out_y);
+
+typedef uint32_t (*DvrHwcFrameGetLayerTransformPtr)(DvrHwcFrame* frame,
+                                                    size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerDataspacePtr)(DvrHwcFrame* frame,
+                                                    size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerColorPtr)(DvrHwcFrame* frame,
+                                                size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerNumVisibleRegionsPtr)(DvrHwcFrame* frame,
+                                                            size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerVisibleRegionPtr)(DvrHwcFrame* frame,
+                                                           size_t layer_index,
+                                                           size_t index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerNumDamagedRegionsPtr)(DvrHwcFrame* frame,
+                                                            size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame,
+                                                           size_t layer_index,
+                                                           size_t index);
+
+// The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
+// will populate. A DvrWriteBufferQueue must be created with this metadata iff
+// ANativeWindow access is needed. Please do not remove, modify, or reorder
+// 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 {
+  // Timestamp of the frame.
+  int64_t timestamp;
+
+  // Whether the buffer is using auto timestamp.
+  int32_t is_auto_timestamp;
+
+  // Must be one of the HAL_DATASPACE_XXX value defined in system/graphics.h
+  int32_t dataspace;
+
+  // Crop extracted from an ACrop or android::Crop object.
+  int32_t crop_left;
+  int32_t crop_top;
+  int32_t crop_right;
+  int32_t crop_bottom;
+
+  // Must be one of the NATIVE_WINDOW_SCALING_MODE_XXX value defined in
+  // system/window.h.
+  int32_t scaling_mode;
+
+  // Must be one of the ANATIVEWINDOW_TRANSFORM_XXX value defined in
+  // android/native_window.h
+  int32_t transform;
+
+  // Reserved bytes for so that the struct is forward compatible.
+  int32_t reserved[16];
+};
+
+struct DvrApi_v1 {
+// Defines an API entry for V1 (no version suffix).
+#define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name
+
+// Include file with API entries.
+#include "dvr_api_entries.h"
+
+// Undefine macro definitions to play nice with Google3 style rules.
+#undef DVR_V1_API_ENTRY
+};
+
+int dvrGetApi(void* api, size_t struct_size, int version);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_API_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
new file mode 100644
index 0000000..09568fd
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -0,0 +1,135 @@
+// dvr_api_entries.h
+//
+// Defines the DVR platform library API entries.
+//
+// Do not include this header directly.
+
+#ifndef DVR_V1_API_ENTRY
+#error Do not include this header directly.
+#endif
+
+// Display manager client
+DVR_V1_API_ENTRY(DisplayManagerCreate);
+DVR_V1_API_ENTRY(DisplayManagerDestroy);
+DVR_V1_API_ENTRY(DisplayManagerSetupNamedBuffer);
+DVR_V1_API_ENTRY(DisplayManagerGetEventFd);
+DVR_V1_API_ENTRY(DisplayManagerTranslateEpollEventMask);
+DVR_V1_API_ENTRY(DisplayManagerGetSurfaceState);
+DVR_V1_API_ENTRY(DisplayManagerGetReadBufferQueue);
+DVR_V1_API_ENTRY(SurfaceStateCreate);
+DVR_V1_API_ENTRY(SurfaceStateDestroy);
+DVR_V1_API_ENTRY(SurfaceStateGetSurfaceCount);
+DVR_V1_API_ENTRY(SurfaceStateGetUpdateFlags);
+DVR_V1_API_ENTRY(SurfaceStateGetSurfaceId);
+DVR_V1_API_ENTRY(SurfaceStateGetProcessId);
+DVR_V1_API_ENTRY(SurfaceStateGetQueueCount);
+DVR_V1_API_ENTRY(SurfaceStateGetQueueIds);
+DVR_V1_API_ENTRY(SurfaceStateGetZOrder);
+DVR_V1_API_ENTRY(SurfaceStateGetVisible);
+DVR_V1_API_ENTRY(SurfaceStateGetAttributeCount);
+DVR_V1_API_ENTRY(SurfaceStateGetAttributes);
+
+// Write buffer
+DVR_V1_API_ENTRY(WriteBufferCreateEmpty);
+DVR_V1_API_ENTRY(WriteBufferDestroy);
+DVR_V1_API_ENTRY(WriteBufferIsValid);
+DVR_V1_API_ENTRY(WriteBufferClear);
+DVR_V1_API_ENTRY(WriteBufferGetId);
+DVR_V1_API_ENTRY(WriteBufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(WriteBufferPost);
+DVR_V1_API_ENTRY(WriteBufferGain);
+DVR_V1_API_ENTRY(WriteBufferGainAsync);
+DVR_V1_API_ENTRY(WriteBufferGetNativeHandle);
+
+// Read buffer
+DVR_V1_API_ENTRY(ReadBufferCreateEmpty);
+DVR_V1_API_ENTRY(ReadBufferDestroy);
+DVR_V1_API_ENTRY(ReadBufferIsValid);
+DVR_V1_API_ENTRY(ReadBufferClear);
+DVR_V1_API_ENTRY(ReadBufferGetId);
+DVR_V1_API_ENTRY(ReadBufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(ReadBufferAcquire);
+DVR_V1_API_ENTRY(ReadBufferRelease);
+DVR_V1_API_ENTRY(ReadBufferReleaseAsync);
+DVR_V1_API_ENTRY(ReadBufferGetNativeHandle);
+
+// Buffer
+DVR_V1_API_ENTRY(BufferDestroy);
+DVR_V1_API_ENTRY(BufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(BufferGetNativeHandle);
+
+// Write buffer queue
+DVR_V1_API_ENTRY(WriteBufferQueueDestroy);
+DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity);
+DVR_V1_API_ENTRY(WriteBufferQueueGetId);
+DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface);
+DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue);
+DVR_V1_API_ENTRY(WriteBufferQueueDequeue);
+
+// Read buffer queue
+DVR_V1_API_ENTRY(ReadBufferQueueDestroy);
+DVR_V1_API_ENTRY(ReadBufferQueueGetCapacity);
+DVR_V1_API_ENTRY(ReadBufferQueueGetId);
+DVR_V1_API_ENTRY(ReadBufferQueueCreateReadQueue);
+DVR_V1_API_ENTRY(ReadBufferQueueDequeue);
+
+// V-Sync client
+DVR_V1_API_ENTRY(VSyncClientCreate);
+DVR_V1_API_ENTRY(VSyncClientDestroy);
+DVR_V1_API_ENTRY(VSyncClientGetSchedInfo);
+
+// Display surface
+DVR_V1_API_ENTRY(SurfaceCreate);
+DVR_V1_API_ENTRY(SurfaceDestroy);
+DVR_V1_API_ENTRY(SurfaceGetId);
+DVR_V1_API_ENTRY(SurfaceSetAttributes);
+DVR_V1_API_ENTRY(SurfaceCreateWriteBufferQueue);
+DVR_V1_API_ENTRY(GetNamedBuffer);
+
+// Pose client
+DVR_V1_API_ENTRY(PoseCreate);
+DVR_V1_API_ENTRY(PoseDestroy);
+DVR_V1_API_ENTRY(PoseGet);
+DVR_V1_API_ENTRY(PoseGetVsyncCount);
+DVR_V1_API_ENTRY(PoseGetController);
+
+// Virtual touchpad client
+DVR_V1_API_ENTRY(VirtualTouchpadCreate);
+DVR_V1_API_ENTRY(VirtualTouchpadDestroy);
+DVR_V1_API_ENTRY(VirtualTouchpadAttach);
+DVR_V1_API_ENTRY(VirtualTouchpadDetach);
+DVR_V1_API_ENTRY(VirtualTouchpadTouch);
+DVR_V1_API_ENTRY(VirtualTouchpadButtonState);
+
+// VR HWComposer client
+DVR_V1_API_ENTRY(HwcClientCreate);
+DVR_V1_API_ENTRY(HwcClientDestroy);
+DVR_V1_API_ENTRY(HwcFrameDestroy);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayId);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayWidth);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayHeight);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayRemoved);
+DVR_V1_API_ENTRY(HwcFrameGetActiveConfig);
+DVR_V1_API_ENTRY(HwcFrameGetColorMode);
+DVR_V1_API_ENTRY(HwcFrameGetColorTransform);
+DVR_V1_API_ENTRY(HwcFrameGetPowerMode);
+DVR_V1_API_ENTRY(HwcFrameGetVsyncEnabled);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCount);
+DVR_V1_API_ENTRY(HwcFrameGetLayerId);
+DVR_V1_API_ENTRY(HwcFrameGetLayerBuffer);
+DVR_V1_API_ENTRY(HwcFrameGetLayerFence);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDisplayFrame);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCrop);
+DVR_V1_API_ENTRY(HwcFrameGetLayerBlendMode);
+DVR_V1_API_ENTRY(HwcFrameGetLayerAlpha);
+DVR_V1_API_ENTRY(HwcFrameGetLayerType);
+DVR_V1_API_ENTRY(HwcFrameGetLayerApplicationId);
+DVR_V1_API_ENTRY(HwcFrameGetLayerZOrder);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCursor);
+DVR_V1_API_ENTRY(HwcFrameGetLayerTransform);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDataspace);
+DVR_V1_API_ENTRY(HwcFrameGetLayerColor);
+DVR_V1_API_ENTRY(HwcFrameGetLayerNumVisibleRegions);
+DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion);
+DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer.h b/libs/vr/libdvr/include/dvr/dvr_buffer.h
new file mode 100644
index 0000000..af55698
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer.h
@@ -0,0 +1,104 @@
+#ifndef ANDROID_DVR_BUFFER_H_
+#define ANDROID_DVR_BUFFER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <memory>
+
+__BEGIN_DECLS
+
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrBuffer DvrBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+struct native_handle;
+
+// Creates an empty write buffer that may be filled with an acutal buffer by
+// other functions.
+void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer);
+
+// Destroys the write buffer.
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer);
+
+// Returns 1 if the given write buffer object contains a buffer, 0 otherwise.
+int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer);
+
+// Clears the contents of the buffer object. After a call to this function
+// dvrWriteBufferIsValid on the same buffer object returns 0.
+int dvrWriteBufferClear(DvrWriteBuffer* write_buffer);
+
+// Returns the global BufferHub id of this buffer.
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer);
+
+// Returns an AHardwareBuffer for the underlying buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+                                     AHardwareBuffer** hardware_buffer);
+
+// Posts the buffer, notifying any connected read buffers. Takes ownership of
+// |ready_fence_fd|.
+int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
+                       const void* meta, size_t meta_size_bytes);
+
+// Gains a buffer that has been released by all connected read buffers.
+int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd);
+int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+    DvrWriteBuffer* write_buffer);
+
+// Creates an empty read buffer that may be filled with and actual buffer by
+// other functions.
+void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer);
+
+// Destroys the read buffer.
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
+
+// Returns 1 if the given write buffer object contains a buffer, 0 otherwise.
+int dvrReadBufferIsValid(DvrReadBuffer* read_buffer);
+
+// Clears the contents of the buffer object. After a call to this function
+// dvrReadBufferIsValid on the same buffer object returns 0.
+int dvrReadBufferClear(DvrReadBuffer* read_buffer);
+
+// Returns the global BufferHub id of this buffer.
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer);
+
+// Returns an AHardwareBuffer for the underlying buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+                                    AHardwareBuffer** hardware_buffer);
+
+// Acquires the read buffer after it has been posted by the write buffer it is
+// connected to.
+int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
+                         void* meta, size_t meta_size_bytes);
+
+// Releases the read buffer, notifying the write buffer it is connected to.
+// Takes ownership of |release_fence_fd|.
+int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd);
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrReadBufferGetNativeHandle(
+    DvrReadBuffer* read_buffer);
+
+// Destroys the buffer.
+void dvrBufferDestroy(DvrBuffer* buffer);
+
+// Gets an AHardwareBuffer from the buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+                                AHardwareBuffer** hardware_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_BUFFER_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
new file mode 100644
index 0000000..dd669dc
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_BUFFER_QUEUE_H_
+#define ANDROID_DVR_BUFFER_QUEUE_H_
+
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_buffer.h>
+
+__BEGIN_DECLS
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+// WriteBufferQueue
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue);
+ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue);
+int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue);
+
+// Returns ANativeWindow. Can be casted to a Java Surface using
+// ANativeWindow_toSurface NDK API. Note that this method does not acquire an
+// additional reference to the ANativeWindow returned, don't call
+// ANativeWindow_release on it.
+int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
+                                          ANativeWindow** out_window);
+
+int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
+                                       DvrReadBufferQueue** out_read_queue);
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+                               DvrWriteBuffer* out_buffer, int* out_fence_fd);
+
+// ReadeBufferQueue
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue);
+ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue);
+int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue);
+int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
+                                      DvrReadBufferQueue** out_read_queue);
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+                              DvrReadBuffer* out_buffer, int* out_fence_fd,
+                              void* out_meta, size_t meta_size_bytes);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_BUFFER_QUEUE_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h
new file mode 100644
index 0000000..943384f
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_DELETER_H_
+#define ANDROID_DVR_DELETER_H_
+
+#include <sys/cdefs.h>
+
+#include <memory>
+
+// Header-only C++ helper to delete opaque DVR objects.
+
+__BEGIN_DECLS
+
+// Use forward declarations to avoid dependency on other headers.
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrSurface DvrSurface;
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+typedef struct DvrVSyncClient DvrVSyncClient;
+
+void dvrBufferDestroy(DvrBuffer* buffer);
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer);
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue);
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue);
+void dvrDisplayManagerDestroy(DvrDisplayManager* client);
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state);
+void dvrSurfaceDestroy(DvrSurface* surface);
+void dvrHwcClientDestroy(DvrHwcClient* client);
+void dvrHwcFrameDestroy(DvrHwcFrame* frame);
+void dvrVSyncClientDestroy(DvrVSyncClient* client);
+
+__END_DECLS
+
+// Avoid errors if this header is included in C code.
+#if defined(__cplusplus)
+
+namespace android {
+namespace dvr {
+
+// Universal DVR object deleter. May be passed to smart pointer types to handle
+// deletion of DVR API objects.
+struct DvrObjectDeleter {
+  void operator()(DvrBuffer* p) { dvrBufferDestroy(p); }
+  void operator()(DvrReadBuffer* p) { dvrReadBufferDestroy(p); }
+  void operator()(DvrWriteBuffer* p) { dvrWriteBufferDestroy(p); }
+  void operator()(DvrReadBufferQueue* p) { dvrReadBufferQueueDestroy(p); }
+  void operator()(DvrWriteBufferQueue* p) { dvrWriteBufferQueueDestroy(p); }
+  void operator()(DvrDisplayManager* p) { dvrDisplayManagerDestroy(p); }
+  void operator()(DvrSurfaceState* p) { dvrSurfaceStateDestroy(p); }
+  void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); }
+  void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); }
+  void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); }
+  void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); }
+};
+
+// Helper to define unique pointers for DVR object types.
+template <typename T>
+using MakeUniqueDvrPointer = std::unique_ptr<T, DvrObjectDeleter>;
+
+// Unique pointer types for DVR objects.
+using UniqueDvrBuffer = MakeUniqueDvrPointer<DvrBuffer>;
+using UniqueDvrReadBuffer = MakeUniqueDvrPointer<DvrReadBuffer>;
+using UniqueDvrWriteBuffer = MakeUniqueDvrPointer<DvrWriteBuffer>;
+using UniqueDvrReadBufferQueue = MakeUniqueDvrPointer<DvrReadBufferQueue>;
+using UniqueDvrWriteBufferQueue = MakeUniqueDvrPointer<DvrWriteBufferQueue>;
+using UniqueDvrDisplayManager = MakeUniqueDvrPointer<DvrDisplayManager>;
+using UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>;
+using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>;
+using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>;
+using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>;
+using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>;
+
+// TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into
+// the relevant constructors.
+
+}  // namespace dvr
+}  // namespace android
+
+#endif // defined(__cplusplus)
+
+#endif  // ANDROID_DVR_DELETER_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_display_manager.h b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
new file mode 100644
index 0000000..d5aef8b
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
@@ -0,0 +1,140 @@
+#ifndef DVR_DISPLAY_MANAGER_CLIENT_H_
+#define DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_display_types.h>
+#include <dvr/dvr_surface.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+typedef uint64_t DvrSurfaceUpdateFlags;
+
+// Attempts to connect to the display manager service.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerCreate(DvrDisplayManager** client_out);
+
+// Destroys the display manager client object.
+void dvrDisplayManagerDestroy(DvrDisplayManager* client);
+
+// Sets up a named buffer for shared memory data transfer between display
+// clients and the display manager.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client,
+                                      const char* name, size_t size,
+                                      uint64_t usage, DvrBuffer** buffer_out);
+
+// Returns an fd used to signal when surface updates occur. Note that depending
+// on the underlying transport, only a subset of the real event bits may be
+// supported. Use dvrDisplayManagerClientTranslateEpollEventMask to get the real
+// event flags.
+// @return the fd on success. Otherwise returns a negative error value.
+int dvrDisplayManagerGetEventFd(DvrDisplayManager* client);
+
+// @param in_events pass in the epoll revents that were initially returned by
+// poll/epoll.
+// @param on success, this value will be overwritten with the true poll/epoll
+// values.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
+                                             int in_events, int* out_events);
+
+// Queries the display manager service for the current state of the display
+// surfaces and stores the results in the given surface state object.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
+                                     DvrSurfaceState* surface_state);
+
+// Gets a read buffer queue from the surface |surface_id| named |queue_id|. Each
+// call returns a different read buffer queue connected to the same write buffer
+// queue. Callers should cache these instead of requesting new ones when
+// possible.
+int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
+                                        int surface_id, int queue_id,
+                                        DvrReadBufferQueue** queue_out);
+
+// Creates a new surface state object. This object may be used to receive the
+// results of a surface state query. More than one state object may be created
+// to keep multiple snapshots, if desired.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateCreate(DvrSurfaceState** surface_state);
+
+// Destorys the surface state object.
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state);
+
+// Writes the number of surfaces described in the state object into |count_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
+                                   size_t* count_out);
+
+// Returns the update flags for the surface at |surface_index| in the state
+// object. The flags may be used to determine what changes, if any, occured to
+// the surface since the last state update.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
+                                  size_t surface_index,
+                                  DvrSurfaceUpdateFlags* flags_out);
+
+// Returns the unique identifier of surface at |surface_index| in the state
+// object. The identifier may be used to distinguish between surfaces.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
+                                size_t surface_index, int* surface_id_out);
+
+// Returns the process id of surface at |surface_index| in the state object.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
+                                size_t surface_index, int* process_id_out);
+
+// Writes the number of queues in the surface at |surface_index| in the state
+// object into |count_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
+                                 size_t surface_index, size_t* count_out);
+
+// Returns up to |max_count| queue ids for the queues belonging to the surface
+// at |surface_index| in the state object.
+// @return The number of queue ids written on success. Otherwise returns a
+// negative error value.
+ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
+                                   size_t surface_index, int* queue_ids,
+                                   size_t max_count);
+
+// Writes the z-order of the surface at |surface_index| in surface state object
+// into |z_order_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
+                             size_t surface_index, int* z_order_out);
+
+// Writes the visible state of the surface at |surface_index| in the surface
+// state object into |visible_out|.
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
+                              size_t surface_index, bool* visible_out);
+
+// Writes the number of attributes on the surface at |surface_index| in the
+// state object into |count_out|.
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
+                                     size_t surface_index, size_t* count_out);
+
+// Writes the list of attribute key/value pairs for the surface at
+// |surface_index| in the surface state object into |attributes|.
+// @return The number of attributes written on success. Otherwise returns a
+// negative error value.
+ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
+                                     size_t surface_index,
+                                     DvrSurfaceAttribute* attributes,
+                                     size_t max_count);
+
+__END_DECLS
+
+#endif  // DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
new file mode 100644
index 0000000..7ee7f9e
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
@@ -0,0 +1,107 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
+
+#include <dvr/dvr_hardware_composer_defs.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+
+// Called when a new frame has arrived.
+//
+// @param client_state Pointer to client state passed in |dvrHwcCreateClient()|.
+// @param frame New frame. Owned by the client.
+// @return fence FD for the release of the last frame.
+typedef int(*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame);
+
+// @param callback Called when a new frame is available.
+// @param client_state Pointer to client state passed back in the callback.
+DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback,
+                                 void* client_state);
+
+void dvrHwcClientDestroy(DvrHwcClient* client);
+
+// Called to free the frame information.
+void dvrHwcFrameDestroy(DvrHwcFrame* frame);
+
+DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame);
+
+int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame);
+
+int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame);
+
+// @return True if the display has been removed. In this case the current frame
+// does not contain any valid layers to display. It is a signal to clean up any
+// display related state.
+bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame);
+
+// @return Number of layers in the frame.
+size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame);
+
+uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame);
+uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame);
+void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
+                                  int32_t* out_hint);
+uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame);
+uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame);
+
+DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index);
+
+// Return the graphic buffer associated with the layer at |layer_index| in
+// |frame|.
+//
+// @return Graphic buffer. Caller owns the buffer and is responsible for freeing
+// it. (see AHardwareBuffer_release())
+AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
+                                           size_t layer_index);
+
+// Returns the fence FD for the layer at index |layer_index| in |frame|.
+//
+// @return Fence FD. Caller owns the FD and is responsible for closing it.
+int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index);
+
+DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
+                                            size_t layer_index);
+
+DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index);
+
+DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
+                                             size_t layer_index);
+
+float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
+                                          size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index);
+
+void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
+                               int32_t* out_x, int32_t* out_y);
+
+uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
+                                              size_t layer_index);
+DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
+                                             size_t layer_index, size_t index);
+
+uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
+                                              size_t layer_index);
+DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
+                                             size_t layer_index, size_t index);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h
new file mode 100644
index 0000000..36c30f9
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h
@@ -0,0 +1,50 @@
+#ifndef ANDROID_VR_HARDWARE_COMPOSER_DEFS_H
+#define ANDROID_VR_HARDWARE_COMPOSER_DEFS_H
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: These definitions must match the ones in
+// //hardware/libhardware/include/hardware/hwcomposer2.h. They are used by the
+// client side which does not have access to hwc2 headers.
+enum DvrHwcBlendMode {
+  DVR_HWC_BLEND_MODE_INVALID = 0,
+  DVR_HWC_BLEND_MODE_NONE = 1,
+  DVR_HWC_BLEND_MODE_PREMULTIPLIED = 2,
+  DVR_HWC_BLEND_MODE_COVERAGE = 3,
+};
+
+enum DvrHwcComposition {
+  DVR_HWC_COMPOSITION_INVALID = 0,
+  DVR_HWC_COMPOSITION_CLIENT = 1,
+  DVR_HWC_COMPOSITION_DEVICE = 2,
+  DVR_HWC_COMPOSITION_SOLID_COLOR = 3,
+  DVR_HWC_COMPOSITION_CURSOR = 4,
+  DVR_HWC_COMPOSITION_SIDEBAND = 5,
+};
+
+typedef uint64_t DvrHwcDisplay;
+typedef uint64_t DvrHwcLayer;
+
+struct DvrHwcRecti {
+  int32_t left;
+  int32_t top;
+  int32_t right;
+  int32_t bottom;
+};
+
+struct DvrHwcRectf {
+  float left;
+  float top;
+  float right;
+  float bottom;
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_DEFS_H
diff --git a/libs/vr/libdvr/include/dvr/dvr_surface.h b/libs/vr/libdvr/include/dvr/dvr_surface.h
new file mode 100644
index 0000000..361488e
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_surface.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_DVR_SURFACE_H_
+#define ANDROID_DVR_SURFACE_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <dvr/dvr_display_types.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrSurface DvrSurface;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+// Attribute types. The values are one-hot encoded to support singluar types or
+// masks of supported types.
+enum {
+  DVR_SURFACE_ATTRIBUTE_TYPE_NONE = 0,
+  DVR_SURFACE_ATTRIBUTE_TYPE_INT32 = (1 << 0),
+  DVR_SURFACE_ATTRIBUTE_TYPE_INT64 = (1 << 1),
+  DVR_SURFACE_ATTRIBUTE_TYPE_BOOL = (1 << 2),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT = (1 << 3),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2 = (1 << 4),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3 = (1 << 5),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4 = (1 << 6),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8 = (1 << 7),
+  DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16 = (1 << 8),
+};
+
+typedef uint64_t DvrSurfaceAttributeType;
+typedef int32_t DvrSurfaceAttributeKey;
+
+typedef struct DvrSurfaceAttributeValue {
+  DvrSurfaceAttributeType type;
+  union {
+    int32_t int32_value;
+    int64_t int64_value;
+    bool bool_value;
+    float float_value;
+    float float2_value[2];
+    float float3_value[3];
+    float float4_value[4];
+    float float8_value[8];
+    float float16_value[16];
+  };
+} DvrSurfaceAttributeValue;
+
+typedef struct DvrSurfaceAttribute {
+  DvrSurfaceAttributeKey key;
+  DvrSurfaceAttributeValue value;
+} DvrSurfaceAttribute;
+
+// Creates a new display surface with the given attributes.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
+                     size_t attribute_count, DvrSurface** surface_out);
+
+// Destroys the display surface.
+void dvrSurfaceDestroy(DvrSurface* surface);
+
+// Gets the DisplayService global id for this surface.
+int dvrSurfaceGetId(DvrSurface* surface);
+
+// Sets attributes on the given display surface.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceSetAttributes(DvrSurface* surface,
+                            const DvrSurfaceAttribute* attributes,
+                            size_t attribute_count);
+
+// Creates a new write-side buffer queue on the given surface. Direct surfaces
+// may only have one queue, the latest call replacing any prior queue. Replaced
+// queues are still referenced and should be destryoed using the queue destroy
+// API.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
+                                     uint32_t height, uint32_t format,
+                                     uint32_t layer_count, uint64_t usage,
+                                     size_t capacity,
+                                     DvrWriteBufferQueue** queue_out);
+
+// Get a named buffer from the display service.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_SURFACE_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h
new file mode 100644
index 0000000..1eea3d9
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_VSYNC_H_
+#define ANDROID_DVR_VSYNC_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrVSyncClient DvrVSyncClient;
+
+// Creates a new client to the system vsync service.
+int dvrVSyncClientCreate(DvrVSyncClient** client_out);
+
+// Destroys the vsync client.
+void dvrVSyncClientDestroy(DvrVSyncClient* client);
+
+// Get the estimated timestamp of the next GPU lens warp preemption event in/
+// ns. Also returns the corresponding vsync count that the next lens warp
+// operation will target.
+int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
+                               int64_t* next_timestamp_ns,
+                               uint32_t* next_vsync_count);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_VSYNC_H_
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
new file mode 100644
index 0000000..ef746e2
--- /dev/null
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -0,0 +1,53 @@
+// 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.
+
+shared_libraries = [
+    "libbase",
+    "libbinder",
+    "libcutils",
+    "libgui",
+    "liblog",
+    "libhardware",
+    "libui",
+    "libutils",
+    "libnativewindow",
+]
+
+static_libraries = [
+    "libdvr_static",
+    "libbufferhubqueue",
+    "libbufferhub",
+    "libchrome",
+    "libdvrcommon",
+    "libdisplay",
+    "libpdx_default_transport",
+]
+
+cc_test {
+    srcs: [
+        "dvr_buffer_queue-test.cpp",
+        "dvr_display_manager-test.cpp",
+        "dvr_named_buffer-test.cpp",
+    ],
+
+    static_libs: static_libraries,
+    shared_libs: shared_libraries,
+    cflags: [
+        "-DLOG_TAG=\"dvr_api-test\"",
+        "-DTRACE=0",
+        "-O0",
+        "-g",
+    ],
+    name: "dvr_api-test",
+}
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
new file mode 100644
index 0000000..474e968
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -0,0 +1,217 @@
+#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"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+static constexpr int kBufferWidth = 100;
+static constexpr int kBufferHeight = 1;
+static constexpr int kLayerCount = 1;
+static constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+static constexpr size_t kQueueCapacity = 3;
+
+typedef uint64_t TestMeta;
+
+class DvrBufferQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    write_queue_ = CreateDvrWriteBufferQueueFromProducerQueue(
+        ProducerQueue::Create<TestMeta>(0, 0, 0, 0));
+    ASSERT_NE(nullptr, write_queue_);
+  }
+
+  void TearDown() override {
+    if (write_queue_ != nullptr) {
+      dvrWriteBufferQueueDestroy(write_queue_);
+      write_queue_ = nullptr;
+    }
+  }
+
+  void AllocateBuffers(size_t buffer_count) {
+    size_t out_slot;
+    for (size_t i = 0; i < buffer_count; i++) {
+      int ret = GetProducerQueueFromDvrWriteBufferQueue(write_queue_)
+                    ->AllocateBuffer(kBufferWidth, kBufferHeight, kLayerCount,
+                                     kBufferFormat, kBufferUsage, &out_slot);
+      ASSERT_EQ(0, ret);
+    }
+  }
+
+  DvrWriteBufferQueue* write_queue_{nullptr};
+};
+
+TEST_F(DvrBufferQueueTest, TestWrite_QueueDestroy) {
+  dvrWriteBufferQueueDestroy(write_queue_);
+  write_queue_ = nullptr;
+}
+
+TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
+  AllocateBuffers(kQueueCapacity);
+  size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
+
+  ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity);
+  ASSERT_EQ(kQueueCapacity, capacity);
+}
+
+TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
+  DvrReadBufferQueue* read_queue = nullptr;
+  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, read_queue);
+
+  dvrReadBufferQueueDestroy(read_queue);
+}
+
+TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
+  DvrReadBufferQueue* read_queue1 = nullptr;
+  DvrReadBufferQueue* read_queue2 = nullptr;
+  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
+
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, read_queue1);
+
+  ret = dvrReadBufferQueueCreateReadQueue(read_queue1, &read_queue2);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, read_queue2);
+  ASSERT_NE(read_queue1, read_queue2);
+
+  dvrReadBufferQueueDestroy(read_queue1);
+  dvrReadBufferQueueDestroy(read_queue2);
+}
+
+TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
+  AllocateBuffers(3);
+
+  DvrReadBuffer* read_buffer = nullptr;
+  DvrWriteBuffer* write_buffer = nullptr;
+
+  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;
+  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));
+}
+
+TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) {
+  static constexpr int kTimeout = 0;
+  DvrReadBufferQueue* read_queue = nullptr;
+  DvrReadBuffer* rb = nullptr;
+  DvrWriteBuffer* wb = nullptr;
+  int fence_fd = -1;
+
+  int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, read_queue);
+
+  dvrWriteBufferCreateEmpty(&wb);
+  ASSERT_NE(nullptr, wb);
+
+  dvrReadBufferCreateEmpty(&rb);
+  ASSERT_NE(nullptr, rb);
+
+  AllocateBuffers(kQueueCapacity);
+
+  // Gain buffer for writing.
+  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd);
+  ASSERT_EQ(0, ret);
+  ASSERT_TRUE(dvrWriteBufferIsValid(wb));
+  ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
+           wb, fence_fd);
+  pdx::LocalHandle 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);
+  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);
+  ASSERT_TRUE(dvrReadBufferIsValid(rb));
+  ASSERT_EQ(seq, acquired_seq);
+  ALOGD_IF(TRACE,
+           "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
+           fence_fd);
+  pdx::LocalHandle acquire_fence(fence_fd);
+
+  // Release buffer to the write_queue.
+  ret = dvrReadBufferRelease(rb, -1);
+  ASSERT_EQ(0, ret);
+  dvrReadBufferDestroy(rb);
+  rb = nullptr;
+
+  // TODO(b/34387835) Currently buffer allocation has to happen after all queues
+  // are initialized.
+  size_t capacity = dvrReadBufferQueueGetCapacity(read_queue);
+
+  ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity);
+  ASSERT_EQ(kQueueCapacity, capacity);
+
+  dvrReadBufferQueueDestroy(read_queue);
+}
+
+TEST_F(DvrBufferQueueTest, TestGetExternalSurface) {
+  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.
+  std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)>
+      write_queue(
+          CreateDvrWriteBufferQueueFromProducerQueue(
+              ProducerQueue::Create<DvrNativeBufferMetadata>(0, 0, 0, 0)),
+          dvrWriteBufferQueueDestroy);
+  ASSERT_NE(nullptr, write_queue.get());
+
+  ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, window);
+
+  sp<Surface> surface = static_cast<Surface*>(window);
+  ASSERT_TRUE(Surface::isValid(surface));
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
new file mode 100644
index 0000000..a2414d6
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -0,0 +1,579 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <poll.h>
+
+#include <android/hardware_buffer.h>
+
+#include <algorithm>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <dvr/dvr_deleter.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+
+#include <pdx/status.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, bool value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
+  attribute.value.bool_value = value;
+  return attribute;
+}
+
+DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, int32_t value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
+  attribute.value.bool_value = value;
+  return attribute;
+}
+
+Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false,
+                                                  int32_t z_order = 0) {
+  DvrSurface* surface = nullptr;
+  DvrSurfaceAttribute attributes[] = {
+      GetAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
+      GetAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
+
+  const int ret = dvrSurfaceCreate(
+      attributes, std::extent<decltype(attributes)>::value, &surface);
+  if (ret < 0)
+    return ErrorStatus(-ret);
+  else
+    return {UniqueDvrSurface(surface)};
+}
+
+Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue(
+    const UniqueDvrSurface& surface, uint32_t width, uint32_t height,
+    uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity) {
+  DvrWriteBufferQueue* queue;
+  const int ret =
+      dvrSurfaceCreateWriteBufferQueue(surface.get(), width, height, format,
+                                       layer_count, usage, capacity, &queue);
+  if (ret < 0)
+    return ErrorStatus(-ret);
+  else
+    return {UniqueDvrWriteBufferQueue(queue)};
+}
+
+class TestDisplayManager {
+ public:
+  TestDisplayManager(UniqueDvrDisplayManager display_manager,
+                     UniqueDvrSurfaceState surface_state)
+      : display_manager_(std::move(display_manager)),
+        surface_state_(std::move(surface_state)) {
+    const int fd = dvrDisplayManagerGetEventFd(display_manager_.get());
+    LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd);
+    display_manager_event_fd_ = fd;
+  }
+
+  Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id,
+                                                      int queue_id) {
+    DvrReadBufferQueue* queue;
+    const int ret = dvrDisplayManagerGetReadBufferQueue(
+        display_manager_.get(), surface_id, queue_id, &queue);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {UniqueDvrReadBufferQueue(queue)};
+  }
+
+  Status<void> UpdateSurfaceState() {
+    const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(),
+                                                     surface_state_.get());
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {};
+  }
+
+  Status<void> WaitForUpdate() {
+    if (display_manager_event_fd_ < 0)
+      return ErrorStatus(-display_manager_event_fd_);
+
+    const int kTimeoutMs = 10000;  // 10s
+    pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
+    const int count = poll(&pfd, 1, kTimeoutMs);
+    if (count < 0)
+      return ErrorStatus(errno);
+    else if (count == 0)
+      return ErrorStatus(ETIMEDOUT);
+
+    int events;
+    const int ret = dvrDisplayManagerTranslateEpollEventMask(
+        display_manager_.get(), pfd.revents, &events);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else if (events & POLLIN)
+      return UpdateSurfaceState();
+    else
+      return ErrorStatus(EPROTO);
+  }
+
+  Status<size_t> GetSurfaceCount() {
+    size_t count = 0;
+    const int ret =
+        dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {count};
+  }
+
+  Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) {
+    DvrSurfaceUpdateFlags update_flags;
+    const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(),
+                                                  surface_index, &update_flags);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {update_flags};
+  }
+
+  Status<int> GetSurfaceId(size_t surface_index) {
+    int surface_id;
+    const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(),
+                                                surface_index, &surface_id);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {surface_id};
+  }
+
+  Status<int> GetProcessId(size_t surface_index) {
+    int process_id;
+    const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(),
+                                                surface_index, &process_id);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+    else
+      return {process_id};
+  }
+
+  Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) {
+    std::vector<DvrSurfaceAttribute> attributes;
+    size_t count = 0;
+    const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(),
+                                                     surface_index, &count);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+
+    attributes.resize(count);
+    const ssize_t return_count = dvrSurfaceStateGetAttributes(
+        surface_state_.get(), surface_index, attributes.data(), count);
+    if (return_count < 0)
+      return ErrorStatus(-return_count);
+
+    attributes.resize(return_count);
+    return {std::move(attributes)};
+  }
+
+  Status<std::vector<int>> GetQueueIds(size_t surface_index) {
+    std::vector<int> queue_ids;
+    size_t count = 0;
+    const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(),
+                                                 surface_index, &count);
+    if (ret < 0)
+      return ErrorStatus(-ret);
+
+    if (count > 0) {
+      queue_ids.resize(count);
+      const ssize_t return_count = dvrSurfaceStateGetQueueIds(
+          surface_state_.get(), surface_index, queue_ids.data(), count);
+      if (return_count < 0)
+        return ErrorStatus(-return_count);
+
+      queue_ids.resize(return_count);
+    }
+
+    return {std::move(queue_ids)};
+  }
+
+ private:
+  UniqueDvrDisplayManager display_manager_;
+  UniqueDvrSurfaceState surface_state_;
+
+  // Owned by object in display_manager_, do not explicitly close.
+  int display_manager_event_fd_;
+
+  TestDisplayManager(const TestDisplayManager&) = delete;
+  void operator=(const TestDisplayManager&) = delete;
+};
+
+class DvrDisplayManagerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    int ret;
+    DvrDisplayManager* display_manager;
+    DvrSurfaceState* surface_state;
+
+    ret = dvrDisplayManagerCreate(&display_manager);
+    ASSERT_EQ(0, ret) << "Failed to create display manager client";
+    ASSERT_NE(nullptr, display_manager);
+
+    ret = dvrSurfaceStateCreate(&surface_state);
+    ASSERT_EQ(0, ret) << "Failed to create surface state object";
+    ASSERT_NE(nullptr, surface_state);
+
+    manager_.reset(
+        new TestDisplayManager(UniqueDvrDisplayManager(display_manager),
+                               UniqueDvrSurfaceState(surface_state)));
+  }
+  void TearDown() override {}
+
+  std::unique_ptr<TestDisplayManager> manager_;
+};
+
+// TODO(eieio): Consider moving these somewhere more central because they are
+// broadly useful.
+
+template <typename T>
+testing::AssertionResult StatusOk(const char* status_expression,
+                                  const Status<T>& status) {
+  if (!status.ok()) {
+    return testing::AssertionFailure()
+           << "(" << status_expression
+           << ") expected to indicate success but actually contains error ("
+           << status.error() << ")";
+  } else {
+    return testing::AssertionSuccess();
+  }
+}
+
+template <typename T>
+testing::AssertionResult StatusError(const char* status_expression,
+                                     const Status<T>& status) {
+  if (status.ok()) {
+    return testing::AssertionFailure()
+           << "(" << status_expression
+           << ") expected to indicate error but instead indicates success.";
+  } else {
+    return testing::AssertionSuccess();
+  }
+}
+
+template <typename T>
+testing::AssertionResult StatusHasError(const char* status_expression,
+                                        const char* /*error_code_expression*/,
+                                        const Status<T>& status,
+                                        int error_code) {
+  if (status.ok()) {
+    return StatusError(status_expression, status);
+  } else if (status.error() != error_code) {
+    return testing::AssertionFailure()
+           << "(" << status_expression << ") expected to indicate error ("
+           << error_code << ") but actually indicates error (" << status.error()
+           << ")";
+  } else {
+    return testing::AssertionSuccess();
+  }
+}
+
+template <typename T, typename U>
+testing::AssertionResult StatusHasValue(const char* status_expression,
+                                        const char* /*value_expression*/,
+                                        const Status<T>& status,
+                                        const U& value) {
+  if (!status.ok()) {
+    return StatusOk(status_expression, status);
+  } else if (status.get() != value) {
+    return testing::AssertionFailure()
+           << "(" << status_expression << ") expected to contain value ("
+           << testing::PrintToString(value) << ") but actually contains value ("
+           << testing::PrintToString(status.get()) << ")";
+  } else {
+    return testing::AssertionSuccess();
+  }
+}
+
+template <typename T, typename Op>
+testing::AssertionResult StatusPred(const char* status_expression,
+                                    const char* pred_expression,
+                                    const Status<T>& status, Op pred) {
+  if (!status.ok()) {
+    return StatusOk(status_expression, status);
+  } else if (!pred(status.get())) {
+    return testing::AssertionFailure()
+           << status_expression << " value ("
+           << testing::PrintToString(status.get())
+           << ") failed to pass predicate " << pred_expression;
+  } else {
+    return testing::AssertionSuccess();
+  }
+}
+
+#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status)
+#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status)
+
+#define ASSERT_STATUS_ERROR_VALUE(value, status) \
+  ASSERT_PRED_FORMAT2(StatusHasError, status, value)
+
+#define ASSERT_STATUS_EQ(value, status) \
+  ASSERT_PRED_FORMAT2(StatusHasValue, status, value)
+
+#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status)
+#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status)
+
+#define EXPECT_STATUS_ERROR_VALUE(value, status) \
+  EXPECT_PRED_FORMAT2(StatusHasError, status, value)
+
+#define EXPECT_STATUS_EQ(value, status) \
+  EXPECT_PRED_FORMAT2(StatusHasValue, status, value)
+
+#define EXPECT_STATUS_PRED(pred, status) \
+  EXPECT_PRED_FORMAT2(StatusPred, status, pred)
+
+#if 0
+// Verify utility predicate/macro functionality. This section is commented out
+// because it is designed to fail in some cases to validate the helpers.
+TEST_F(DvrDisplayManagerTest, ExpectVoid) {
+  Status<void> status_error{ErrorStatus{EINVAL}};
+  Status<void> status_ok{};
+
+  EXPECT_STATUS_ERROR(status_error);
+  EXPECT_STATUS_ERROR(status_ok);
+  EXPECT_STATUS_OK(status_error);
+  EXPECT_STATUS_OK(status_ok);
+
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
+  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
+  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
+}
+
+TEST_F(DvrDisplayManagerTest, ExpectInt) {
+  Status<int> status_error{ErrorStatus{EINVAL}};
+  Status<int> status_ok{10};
+
+  EXPECT_STATUS_ERROR(status_error);
+  EXPECT_STATUS_ERROR(status_ok);
+  EXPECT_STATUS_OK(status_error);
+  EXPECT_STATUS_OK(status_ok);
+
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
+  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
+  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
+
+  EXPECT_STATUS_EQ(10, status_error);
+  EXPECT_STATUS_EQ(20, status_error);
+  EXPECT_STATUS_EQ(10, status_ok);
+  EXPECT_STATUS_EQ(20, status_ok);
+
+  auto pred1 = [](const auto& value) { return value < 15; };
+  auto pred2 = [](const auto& value) { return value > 5; };
+  auto pred3 = [](const auto& value) { return value > 15; };
+  auto pred4 = [](const auto& value) { return value < 5; };
+
+  EXPECT_STATUS_PRED(pred1, status_error);
+  EXPECT_STATUS_PRED(pred2, status_error);
+  EXPECT_STATUS_PRED(pred3, status_error);
+  EXPECT_STATUS_PRED(pred4, status_error);
+  EXPECT_STATUS_PRED(pred1, status_ok);
+  EXPECT_STATUS_PRED(pred2, status_ok);
+  EXPECT_STATUS_PRED(pred3, status_ok);
+  EXPECT_STATUS_PRED(pred4, status_ok);
+}
+#endif
+
+TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
+  // Get surface state and verify there are no surfaces.
+  ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
+  ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+
+  // Get flags for invalid surface index.
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0));
+
+  // Create an application surface.
+  auto surface_status = CreateApplicationSurface();
+  ASSERT_STATUS_OK(surface_status);
+  UniqueDvrSurface surface = surface_status.take();
+  ASSERT_NE(nullptr, surface.get());
+
+  const int surface_id = dvrSurfaceGetId(surface.get());
+  ASSERT_GE(surface_id, 0);
+
+  // Now there should be one new surface.
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Verify the new surface flag is set.
+  auto check_flags = [](const auto& value) {
+    return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE;
+  };
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  // Verify the surface id matches.
+  EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0));
+
+  // Verify the owning process of the surface.
+  EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0));
+
+  surface.reset();
+
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+}
+
+TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
+  // Get surface state and verify there are no surfaces.
+  ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
+  ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+
+  // Get attributes for an invalid surface index.
+  EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0));
+
+  const bool kInitialVisibility = true;
+  const int32_t kInitialZOrder = 10;
+  auto surface_status =
+      CreateApplicationSurface(kInitialVisibility, kInitialZOrder);
+  ASSERT_STATUS_OK(surface_status);
+  auto surface = surface_status.take();
+  ASSERT_NE(nullptr, surface.get());
+
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Check the initial attribute values.
+  auto attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  auto attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), 2u);
+
+  const std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+                                           DVR_SURFACE_ATTRIBUTE_VISIBLE};
+
+  // Collect all the keys in attributes that match the expected keys.
+  std::set<int32_t> actual_keys;
+  std::for_each(attributes.begin(), attributes.end(),
+                [&expected_keys, &actual_keys](const auto& attribute) {
+                  if (expected_keys.find(attribute.key) != expected_keys.end())
+                    actual_keys.emplace(attribute.key);
+                });
+
+  // If the sets match then attributes contained at least the expected keys,
+  // even if other keys were also present.
+  EXPECT_EQ(expected_keys, actual_keys);
+}
+
+TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
+  // Create an application surface.
+  auto surface_status = CreateApplicationSurface();
+  ASSERT_STATUS_OK(surface_status);
+  UniqueDvrSurface surface = surface_status.take();
+  ASSERT_NE(nullptr, surface.get());
+
+  const int surface_id = dvrSurfaceGetId(surface.get());
+  ASSERT_GE(surface_id, 0);
+  // Get surface state and verify there is one surface.
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Verify there are no queues for the surface recorded in the state snapshot.
+  EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));
+
+  // Create a new queue in the surface.
+  auto write_queue_status = CreateSurfaceQueue(
+      surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1,
+      AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1);
+  ASSERT_STATUS_OK(write_queue_status);
+  UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
+  ASSERT_NE(nullptr, write_queue.get());
+
+  const int queue_id = dvrWriteBufferQueueGetId(write_queue.get());
+  ASSERT_GE(queue_id, 0);
+
+  // Update surface state.
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Verify the buffers changed flag is set.
+  auto check_flags = [](const auto& value) {
+    return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED;
+  };
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  auto queue_ids_status = manager_->GetQueueIds(0);
+  ASSERT_STATUS_OK(queue_ids_status);
+
+  auto queue_ids = queue_ids_status.take();
+  ASSERT_EQ(1u, queue_ids.size());
+  EXPECT_EQ(queue_id, queue_ids[0]);
+
+  auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id);
+  ASSERT_STATUS_OK(read_queue_status);
+  UniqueDvrReadBufferQueue read_queue = read_queue_status.take();
+  ASSERT_NE(nullptr, read_queue.get());
+  EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get()));
+
+  write_queue.reset();
+
+  // Verify that destroying the queue generates a surface update event.
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Verify that the buffers changed flag is set.
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  // Verify that the queue ids reflect the change.
+  queue_ids_status = manager_->GetQueueIds(0);
+  ASSERT_STATUS_OK(queue_ids_status);
+
+  queue_ids = queue_ids_status.take();
+  ASSERT_EQ(0u, queue_ids.size());
+}
+
+TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
+  // Create an application surface.
+  auto surface_status = CreateApplicationSurface();
+  ASSERT_STATUS_OK(surface_status);
+  UniqueDvrSurface surface = surface_status.take();
+  ASSERT_NE(nullptr, surface.get());
+
+  // Get surface state and verify there is one surface.
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+  // Create a new queue in the surface.
+  const uint32_t kLayerCount = 3;
+  auto write_queue_status = CreateSurfaceQueue(
+      surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount,
+      AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1);
+  ASSERT_STATUS_OK(write_queue_status);
+  UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
+  ASSERT_NE(nullptr, write_queue.get());
+
+  DvrWriteBuffer* buffer = nullptr;
+  dvrWriteBufferCreateEmpty(&buffer);
+  int fence_fd = -1;
+  int error =
+      dvrWriteBufferQueueDequeue(write_queue.get(), 1000, buffer, &fence_fd);
+  ASSERT_EQ(0, error);
+
+  AHardwareBuffer* hardware_buffer = nullptr;
+  error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer);
+  ASSERT_EQ(0, error);
+
+  AHardwareBuffer_Desc desc = {};
+  AHardwareBuffer_describe(hardware_buffer, &desc);
+  ASSERT_EQ(kLayerCount, desc.layers);
+
+  AHardwareBuffer_release(hardware_buffer);
+  dvrWriteBufferDestroy(buffer);
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
new file mode 100644
index 0000000..e65f6d5
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -0,0 +1,160 @@
+#include <android/hardware_buffer.h>
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+#include <system/graphics.h>
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class DvrNamedBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    const int ret = dvrDisplayManagerCreate(&client_);
+    ASSERT_EQ(0, ret);
+    ASSERT_NE(nullptr, client_);
+  }
+
+  void TearDown() override {
+    dvrDisplayManagerDestroy(client_);
+    client_ = nullptr;
+  }
+
+  DvrDisplayManager* client_ = nullptr;
+};
+
+TEST_F(DvrNamedBufferTest, TestNamedBuffersSameName) {
+  const char* buffer_name = "same_name";
+  DvrBuffer* buffer1 = nullptr;
+  int ret1 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer1);
+  ASSERT_EQ(0, ret1);
+  ASSERT_NE(nullptr, buffer1);
+
+  DvrBuffer* buffer2 = nullptr;
+  int ret2 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer2);
+  ASSERT_EQ(0, ret1);
+  ASSERT_NE(nullptr, buffer2);
+
+  AHardwareBuffer* hardware_buffer1 = nullptr;
+  int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1);
+  ASSERT_EQ(0, e1);
+  ASSERT_NE(nullptr, hardware_buffer1);
+
+  AHardwareBuffer* hardware_buffer2 = nullptr;
+  int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2);
+  ASSERT_EQ(0, e2);
+  ASSERT_NE(nullptr, hardware_buffer2);
+
+  AHardwareBuffer_Desc desc1 = {};
+  AHardwareBuffer_describe(hardware_buffer1, &desc1);
+  AHardwareBuffer_Desc desc2 = {};
+  AHardwareBuffer_describe(hardware_buffer2, &desc2);
+  ASSERT_EQ(desc1.width, 10u);
+  ASSERT_EQ(desc1.height, 1u);
+  ASSERT_EQ(desc1.layers, 1u);
+  ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB);
+  ASSERT_EQ(desc1.usage, 0u);
+  ASSERT_EQ(desc2.width, 10u);
+  ASSERT_EQ(desc2.height, 1u);
+  ASSERT_EQ(desc2.layers, 1u);
+  ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB);
+  ASSERT_EQ(desc2.usage, 0u);
+
+  dvrBufferDestroy(buffer1);
+  dvrBufferDestroy(buffer2);
+
+  DvrBuffer* buffer3 = nullptr;
+  int e3 = dvrGetNamedBuffer(buffer_name, &buffer3);
+  ASSERT_NE(nullptr, buffer3);
+  ASSERT_EQ(0, e3);
+
+  AHardwareBuffer* hardware_buffer3 = nullptr;
+  int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3);
+  ASSERT_EQ(0, e4);
+  ASSERT_NE(nullptr, hardware_buffer3);
+
+  AHardwareBuffer_Desc desc3 = {};
+  AHardwareBuffer_describe(hardware_buffer3, &desc3);
+  ASSERT_EQ(desc3.width, 10u);
+  ASSERT_EQ(desc3.height, 1u);
+  ASSERT_EQ(desc3.layers, 1u);
+  ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB);
+  ASSERT_EQ(desc3.usage, 0u);
+
+  dvrBufferDestroy(buffer3);
+
+  AHardwareBuffer_release(hardware_buffer1);
+  AHardwareBuffer_release(hardware_buffer2);
+  AHardwareBuffer_release(hardware_buffer3);
+}
+
+TEST_F(DvrNamedBufferTest, TestMultipleNamedBuffers) {
+  const char* buffer_name1 = "test1";
+  const char* buffer_name2 = "test2";
+  DvrBuffer* setup_buffer1 = nullptr;
+  int ret1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name1, 10, 0,
+                                               &setup_buffer1);
+  ASSERT_EQ(0, ret1);
+  ASSERT_NE(nullptr, setup_buffer1);
+  dvrBufferDestroy(setup_buffer1);
+
+  DvrBuffer* setup_buffer2 = nullptr;
+  int ret2 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name2, 10, 0,
+                                               &setup_buffer2);
+  ASSERT_EQ(0, ret2);
+  ASSERT_NE(nullptr, setup_buffer2);
+  dvrBufferDestroy(setup_buffer2);
+
+  DvrBuffer* buffer1 = nullptr;
+  int e1 = dvrGetNamedBuffer(buffer_name1, &buffer1);
+  ASSERT_NE(nullptr, buffer1);
+  ASSERT_EQ(0, e1);
+  dvrBufferDestroy(buffer1);
+
+  DvrBuffer* buffer2 = nullptr;
+  int e2 = dvrGetNamedBuffer(buffer_name2, &buffer2);
+  ASSERT_NE(nullptr, buffer2);
+  ASSERT_EQ(0, e2);
+  dvrBufferDestroy(buffer2);
+}
+
+TEST_F(DvrNamedBufferTest, TestNamedBufferUsage) {
+  const char* buffer_name = "buffer_usage";
+
+  // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because
+  // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to
+  // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different.
+  // If all is good, when we get the AHardwareBuffer, it should be converted
+  // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE.
+  const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE;
+
+  DvrBuffer* setup_buffer = nullptr;
+  int e1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, usage,
+                                             &setup_buffer);
+  ASSERT_NE(nullptr, setup_buffer);
+  ASSERT_EQ(0, e1);
+
+  AHardwareBuffer* hardware_buffer = nullptr;
+  int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
+  ASSERT_EQ(0, e2);
+  ASSERT_NE(nullptr, hardware_buffer);
+
+  AHardwareBuffer_Desc desc = {};
+  AHardwareBuffer_describe(hardware_buffer, &desc);
+  ASSERT_EQ(usage, desc.usage);
+
+  dvrBufferDestroy(setup_buffer);
+  AHardwareBuffer_release(hardware_buffer);
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
new file mode 100644
index 0000000..527cdbd
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+localIncludeFiles = [
+    "include",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libutils",
+    "libEGL",
+    "libGLESv2",
+    "libui",
+    "libgui",
+    "libhardware",
+]
+
+staticLibraries = ["libpdx_default_transport"]
+
+headerLibraries = [
+    "libeigen",
+]
+
+cc_library {
+    local_include_dirs: localIncludeFiles,
+
+    cflags: [
+        "-DLOG_TAG=\"libdvrcommon\"",
+        "-DTRACE=0",
+    ],
+    export_include_dirs: localIncludeFiles,
+
+    header_libs: headerLibraries,
+    export_header_lib_headers: headerLibraries,
+
+    name: "libdvrcommon",
+}
+
+testFiles = [
+    "tests/numeric_test.cpp",
+    "tests/pose_test.cpp",
+]
+
+cc_test {
+    name: "libdvrcommon_test",
+    tags: ["optional"],
+
+    srcs: testFiles,
+
+    shared_libs: sharedLibraries,
+
+    static_libs: [
+        "libgmock_main",
+        "libgmock",
+        "libgtest",
+	"libdvrcommon",
+    ] + staticLibraries,
+}
diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
new file mode 100644
index 0000000..7eeab16
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_BENCHMARK_H_
+#define ANDROID_DVR_BENCHMARK_H_
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/trace.h>
+
+#include <private/dvr/clock_ns.h>
+
+// Set benchmark traces, using Android systrace.
+//
+// The simplest one-parameter version of btrace automatically sets the
+// timestamp with the system clock. The other versions can optionally set the
+// timestamp manually, or pass additional data to be written to the log line.
+//
+// Example:
+// Btrace("Start execution");
+// ... code to benchmark ...
+// Btrace("End execution");
+//
+// Use compute_benchmarks.py
+// with the trace path "Start execution,End execution",
+// to report the elapsed time between the two calls.
+//
+// Btrace will either output to standard atrace, or to a file if specified.
+// The versions BtraceData also allow an int64_t to be included in the trace.
+
+// Btrace without data payload.
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic);
+static inline void Btrace(const char* name);
+static inline void Btrace(FILE* file, const char* name,
+                          int64_t nanoseconds_monotonic);
+static inline void Btrace(FILE* file, const char* name);
+
+// Btrace with data payload.
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+                              int64_t data);
+static inline void BtraceData(const char* name, int64_t data);
+static inline void BtraceData(FILE* file, const char* name,
+                              int64_t nanoseconds_monotonic, int64_t data);
+static inline void BtraceData(FILE* file, const char* name, int64_t data);
+
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) {
+  const int kLogMessageLength = 256;
+  char log_message[kLogMessageLength];
+  snprintf(log_message, kLogMessageLength, "#btrace#%s", name);
+  atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void Btrace(const char* name) {
+  Btrace(name, android::dvr::GetSystemClockNs());
+}
+
+static inline void Btrace(FILE* file, const char* name,
+                          int64_t nanoseconds_monotonic) {
+  fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic);
+}
+
+static inline void Btrace(FILE* file, const char* name) {
+  Btrace(file, name, android::dvr::GetSystemClockNs());
+}
+
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+                              int64_t data) {
+  const int kLogMessageLength = 256;
+  char log_message[kLogMessageLength];
+  snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data);
+  atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void BtraceData(const char* name, int64_t data) {
+  BtraceData(name, android::dvr::GetSystemClockNs(), data);
+}
+
+static inline void BtraceData(FILE* file, const char* name,
+                              int64_t nanoseconds_monotonic, int64_t data) {
+  fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data,
+          nanoseconds_monotonic);
+}
+
+static inline void BtraceData(FILE* file, const char* name, int64_t data) {
+  BtraceData(file, name, android::dvr::GetSystemClockNs(), data);
+}
+
+#endif  // ANDROID_DVR_BENCHMARK_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
new file mode 100644
index 0000000..8e777ed
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_CLOCK_NS_H_
+#define ANDROID_DVR_CLOCK_NS_H_
+
+#include <stdint.h>
+#include <time.h>
+
+namespace android {
+namespace dvr {
+
+constexpr int64_t kNanosPerSecond = 1000000000ll;
+
+// Returns the standard Dream OS monotonic system time that corresponds with all
+// timestamps found in Dream OS APIs.
+static inline timespec GetSystemClock() {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  return t;
+}
+
+static inline timespec GetSystemClockRaw() {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+  return t;
+}
+
+static inline int64_t GetSystemClockNs() {
+  timespec t = GetSystemClock();
+  int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+  return ns;
+}
+
+static inline int64_t GetSystemClockRawNs() {
+  timespec t = GetSystemClockRaw();
+  int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+  return ns;
+}
+
+static inline double NsToSec(int64_t nanoseconds) {
+  return nanoseconds / static_cast<double>(kNanosPerSecond);
+}
+
+static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); }
+
+static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; }
+
+// Converts a nanosecond timestamp to a timespec. Based on the kernel function
+// of the same name.
+static inline timespec NsToTimespec(int64_t ns) {
+  timespec t;
+  int32_t remainder;
+
+  t.tv_sec = ns / kNanosPerSecond;
+  remainder = ns % kNanosPerSecond;
+  if (remainder < 0) {
+    t.tv_nsec--;
+    remainder += kNanosPerSecond;
+  }
+  t.tv_nsec = remainder;
+
+  return t;
+}
+
+// Timestamp comparison functions that handle wrapping values correctly.
+static inline bool TimestampLT(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) < 0;
+}
+static inline bool TimestampLE(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) <= 0;
+}
+static inline bool TimestampGT(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) > 0;
+}
+static inline bool TimestampGE(int64_t a, int64_t b) {
+  return static_cast<int64_t>(static_cast<uint64_t>(a) -
+                              static_cast<uint64_t>(b)) >= 0;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_CLOCK_NS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h
new file mode 100644
index 0000000..c31a385
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_DEBUG_H_
+#define ANDROID_DVR_DEBUG_H_
+
+#include <GLES3/gl3.h>
+#include <math.h>
+
+#include <log/log.h>
+
+#ifndef NDEBUG
+#define CHECK_GL()                   \
+  do {                               \
+    const GLenum err = glGetError(); \
+    if (err != GL_NO_ERROR) {        \
+      ALOGE("OpenGL error %d", err); \
+    }                                \
+  } while (0)
+
+#define CHECK_GL_FBO()                                        \
+  do {                                                        \
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+    switch (status) {                                         \
+      case GL_FRAMEBUFFER_COMPLETE:                           \
+        break;                                                \
+      case GL_FRAMEBUFFER_UNSUPPORTED:                        \
+        ALOGE("GL_FRAMEBUFFER_UNSUPPORTED");                  \
+        break;                                                \
+      default:                                                \
+        ALOGE("FBO user error: %d", status);                  \
+        break;                                                \
+    }                                                         \
+  } while (0)
+#else
+#define CHECK_GL()
+#define CHECK_GL_FBO()
+#endif
+
+#endif  // ANDROID_DVR_DEBUG_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
new file mode 100644
index 0000000..defaf58
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_EIGEN_H_
+#define ANDROID_DVR_EIGEN_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace Eigen {
+
+// Eigen doesn't take advantage of C++ template typedefs, but we can
+template <class T, int N>
+using Vector = Matrix<T, N, 1>;
+
+template <class T>
+using Vector2 = Vector<T, 2>;
+
+template <class T>
+using Vector3 = Vector<T, 3>;
+
+template <class T>
+using Vector4 = Vector<T, 4>;
+
+template <class T, int N>
+using RowVector = Matrix<T, 1, N>;
+
+template <class T>
+using RowVector2 = RowVector<T, 2>;
+
+template <class T>
+using RowVector3 = RowVector<T, 3>;
+
+template <class T>
+using RowVector4 = RowVector<T, 4>;
+
+// In Eigen, the type you should be using for transformation matrices is the
+// `Transform` class, instead of a raw `Matrix`.
+// The `Projective` option means this will not make any assumptions about the
+// last row of the object, making this suitable for use as general OpenGL
+// projection matrices (which is the most common use-case). The one caveat
+// is that in order to apply this transformation to non-homogeneous vectors
+// (e.g., vec3), you must use the `.linear()` method to get the affine part of
+// the matrix.
+//
+// Example:
+//   mat4 transform;
+//   vec3 position;
+//   vec3 transformed = transform.linear() * position;
+//
+// Note, the use of N-1 is because the parameter passed to Eigen is the ambient
+// dimension of the transformation, not the size of the matrix iself.
+// However graphics programmers sometimes get upset when they see a 3 next
+// to a matrix when they expect a 4, so I'm hoping this will avoid that.
+template <class T, int N>
+using AffineMatrix = Transform<T, N-1, Projective>;
+
+}  // namespace Eigen
+
+#endif  // ANDROID_DVR_EIGEN_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
new file mode 100644
index 0000000..099a409
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,64 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+
+namespace android {
+namespace dvr {
+
+class EpollFileDescriptor {
+ public:
+  static const int CTL_ADD = EPOLL_CTL_ADD;
+  static const int CTL_MOD = EPOLL_CTL_MOD;
+  static const int CTL_DEL = EPOLL_CTL_DEL;
+
+  EpollFileDescriptor() : fd_(-1) {}
+
+  // Constructs an EpollFileDescriptor from an integer file descriptor and
+  // takes ownership.
+  explicit EpollFileDescriptor(int fd) : fd_(fd) {}
+
+  bool IsValid() const { return fd_.get() >= 0; }
+
+  int Create() {
+    if (IsValid()) {
+      ALOGW("epoll fd has already been created.");
+      return -EALREADY;
+    }
+
+    fd_.reset(epoll_create(64));
+
+    if (fd_.get() < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Control(int op, int target_fd, epoll_event* ev) {
+    if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Wait(epoll_event* events, int maxevents, int timeout) {
+    int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
+
+    if (ret < 0)
+      return -errno;
+    else
+      return ret;
+  }
+
+  int Get() const { return fd_.get(); }
+
+ private:
+  base::unique_fd fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
new file mode 100644
index 0000000..d0ee69c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
@@ -0,0 +1,95 @@
+#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_
+#define ANDROID_DVR_FIELD_OF_VIEW_H_
+
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a generalized, asymmetric field of view with four half angles.
+// Each half angle denotes the angle between the corresponding frustum plane.
+// Together with a near and far plane, a FieldOfView forms the frustum of an
+// off-axis perspective projection.
+class FieldOfView {
+ public:
+  // The default constructor sets an angle of 0 (in any unit) for all four
+  // half-angles.
+  FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {}
+
+  // Constructs a FieldOfView from four angles.
+  FieldOfView(float left, float right, float bottom, float top)
+      : left_(left), right_(right), bottom_(bottom), top_(top) {}
+
+  explicit FieldOfView(const float* fov)
+      : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {}
+
+  // Accessors for all four half-angles.
+  float GetLeft() const { return left_; }
+  float GetRight() const { return right_; }
+  float GetBottom() const { return bottom_; }
+  float GetTop() const { return top_; }
+
+  // Setters for all four half-angles.
+  void SetLeft(float left) { left_ = left; }
+  void SetRight(float right) { right_ = right; }
+  void SetBottom(float bottom) { bottom_ = bottom; }
+  void SetTop(float top) { top_ = top; }
+
+  Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near,
+                                                    float z_far) const {
+    float x_left = -std::tan(left_) * z_near;
+    float x_right = std::tan(right_) * z_near;
+    float y_bottom = -std::tan(bottom_) * z_near;
+    float y_top = std::tan(top_) * z_near;
+
+    float zero = 0.0f;
+    if (x_left == x_right || y_bottom == y_top || z_near == z_far ||
+        z_near <= zero || z_far <= zero) {
+      return Eigen::AffineMatrix<float, 4>::Identity();
+    }
+
+    float x = (2 * z_near) / (x_right - x_left);
+    float y = (2 * z_near) / (y_top - y_bottom);
+    float a = (x_right + x_left) / (x_right - x_left);
+    float b = (y_top + y_bottom) / (y_top - y_bottom);
+    float c = (z_near + z_far) / (z_near - z_far);
+    float d = (2 * z_near * z_far) / (z_near - z_far);
+
+    // Note: Eigen matrix initialization syntax is always 'column-major'
+    // even if the storage is row-major. Or in other words, just write the
+    // matrix like you'd see in a math textbook.
+    Eigen::AffineMatrix<float, 4> result;
+    result.matrix() << x,  0,  a,  0,
+                       0,  y,  b,  0,
+                       0,  0,  c,  d,
+                       0,  0, -1,  0;
+    return result;
+  }
+
+  static FieldOfView FromProjectionMatrix(
+      const Eigen::AffineMatrix<float, 4>& m) {
+    // Compute tangents.
+    float tan_vert_fov = 1.0f / m(1, 1);
+    float tan_horz_fov = 1.0f / m(0, 0);
+    float t = (m(1, 2) + 1.0f) * tan_vert_fov;
+    float b = (m(1, 2) - 1.0f) * tan_vert_fov;
+    float l = (m(0, 2) - 1.0f) * tan_horz_fov;
+    float r = (m(0, 2) + 1.0f) * tan_horz_fov;
+
+    return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b),
+                       std::atan(t));
+  }
+
+ private:
+  float left_;
+  float right_;
+  float bottom_;
+  float top_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FIELD_OF_VIEW_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
new file mode 100644
index 0000000..12ef622
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+#include <ostream>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+
+namespace android {
+namespace dvr {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 2>& vec) {
+  return out << "vec2(" << vec.x() << ',' << vec.y() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 3>& vec) {
+  return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 4>& vec) {
+  return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ','
+             << vec.w() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::AffineMatrix<T, 4>& mat) {
+  out << std::setfill(' ') << std::setprecision(4) << std::fixed
+      << std::showpos;
+  out << "\nmat4[";
+  out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
+      << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " "
+      << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " "
+      << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " "
+      << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3);
+  out << "]\n";
+
+  return out;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) {
+  return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ','
+             << (fov.GetRight() * 180.0f / M_PI) << ','
+             << (fov.GetBottom() * 180.0f / M_PI) << ','
+             << (fov.GetTop() * 180.0f / M_PI) << ')';
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h
new file mode 100644
index 0000000..aef7146
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_MATRIX_HELPERS_H_
+#define ANDROID_DVR_MATRIX_HELPERS_H_
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// A helper function for creating a mat4 directly.
+inline mat4 MakeMat4(float m00, float m01, float m02, float m03, float m10,
+                     float m11, float m12, float m13, float m20, float m21,
+                     float m22, float m23, float m30, float m31, float m32,
+                     float m33) {
+  Eigen::Matrix4f matrix;
+
+  matrix << m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30,
+      m31, m32, m33;
+
+  return mat4(matrix);
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
new file mode 100644
index 0000000..4545893
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
@@ -0,0 +1,175 @@
+#ifndef ANDROID_DVR_NUMERIC_H_
+#define ANDROID_DVR_NUMERIC_H_
+
+#include <cmath>
+#include <limits>
+#include <random>
+#include <type_traits>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FT>
+static inline FT ToDeg(FT f) {
+  return f * static_cast<FT>(180.0 / M_PI);
+}
+
+template <typename FT>
+static inline FT ToRad(FT f) {
+  return f * static_cast<FT>(M_PI / 180.0);
+}
+
+// Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values
+// for example).
+template <typename T>
+T NormalizePeriodicRange(T x, T lo, T hi) {
+  T range_size = hi - lo;
+
+  while (x < lo) {
+    x += range_size;
+  }
+
+  while (x > hi) {
+    x -= range_size;
+  }
+
+  return x;
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range [centre - 180, centre + 180]
+template <typename T>
+T NormalizeDegrees(T x, T centre = static_cast<T>(180.0)) {
+  return NormalizePeriodicRange(x, centre - static_cast<T>(180.0),
+                                centre + static_cast<T>(180.0));
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range
+//         [centre - M_PI, centre + M_PI]
+// @remark the centre parameter is to make it possible to specify a different
+//         periodic range. This is useful if you are planning on comparing two
+//         angles close to 0 or M_PI, so that one might not accidentally end
+//         up on the other side of the range
+template <typename T>
+T NormalizeRadians(T x, T centre = static_cast<T>(M_PI)) {
+  return NormalizePeriodicRange(x, centre - static_cast<T>(M_PI),
+                                centre + static_cast<T>(M_PI));
+}
+
+static inline vec2i Round(const vec2& v) {
+  return vec2i(roundf(v.x()), roundf(v.y()));
+}
+
+static inline vec2i Scale(const vec2i& v, float scale) {
+  return vec2i(roundf(static_cast<float>(v.x()) * scale),
+               roundf(static_cast<float>(v.y()) * scale));
+}
+
+// Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`.
+template <typename T>
+T ConvertRange(T x, T lba, T uba, T lbb, T ubb) {
+  return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb;
+}
+
+template <typename R1, typename R2>
+static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) {
+  vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array());
+  return (normalized * vec2(to.GetSize())) + vec2(to.p1);
+}
+
+template <typename T>
+inline bool IsZero(const T& v,
+                   const T& tol = std::numeric_limits<T>::epsilon()) {
+  return std::abs(v) <= tol;
+}
+
+template <typename T>
+inline bool IsEqual(const T& a, const T& b,
+                    const T& tol = std::numeric_limits<T>::epsilon()) {
+  return std::abs(b - a) <= tol;
+}
+
+template <typename T>
+T Square(const T& x) {
+  return x * x;
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+                typename
+                std::enable_if<std::is_floating_point<T>::value>::type* = 0) {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_real_distribution<T> distro(lo, hi);
+  return distro(gen);
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+                typename
+                std::enable_if<std::is_integral<T>::value>::type* = 0) {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<T> distro(lo, hi);
+  return distro(gen);
+}
+
+template <typename Derived1, typename Derived2>
+Derived1 RandomInRange(
+    const Eigen::MatrixBase<Derived1>& lo,
+    const Eigen::MatrixBase<Derived2>& hi) {
+  EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived1, Derived2);
+
+  Derived1 result = Eigen::MatrixBase<Derived1>::Zero();
+
+  for (int row = 0; row < result.rows(); ++row) {
+    for (int col = 0; col < result.cols(); ++col) {
+      result(row, col) = RandomInRange(lo(row, col), hi(row, col));
+    }
+  }
+
+  return result;
+}
+
+template <typename T>
+T RandomRange(T x) {
+  return RandomInRange(-x, x);
+}
+
+template <typename T>
+T Clamp(T x, T lo, T hi) {
+  return std::min(std::max(x, lo), hi);
+}
+
+inline mat3 ScaleMatrix(const vec2& scale_xy) {
+  return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f));
+}
+
+inline mat3 TranslationMatrix(const vec2& translation) {
+  return mat3(Eigen::Translation2f(translation));
+}
+
+inline mat4 TranslationMatrix(const vec3& translation) {
+  return mat4(Eigen::Translation3f(translation));
+}
+
+inline vec2 TransformPoint(const mat3& m, const vec2& p) {
+  return m.linear() * p + m.translation();
+}
+
+inline vec2 TransformVector(const mat3& m, const vec2& p) {
+  return m.linear() * p;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NUMERIC_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ortho.h b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
new file mode 100644
index 0000000..fc0bce3
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_ORTHO_H_
+#define ANDROID_DVR_ORTHO_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <class T>
+Eigen::AffineMatrix<T, 4> OrthoMatrix(T left, T right, T bottom, T top,
+                                      T znear, T zfar) {
+  Eigen::AffineMatrix<T, 4> result;
+  const T t2 = static_cast<T>(2);
+  const T a = t2 / (right - left);
+  const T b = t2 / (top - bottom);
+  const T c = t2 / (zfar - znear);
+  const T xoff = -(right + left) / (right - left);
+  const T yoff = -(top + bottom) / (top - bottom);
+  const T zoff = -(zfar + znear) / (zfar - znear);
+  const T t1 = static_cast<T>(1);
+  result.matrix() << a, 0, 0, xoff,
+            0, b, 0, yoff,
+            0, 0, c, zoff,
+            0, 0, 0, t1;
+  return result;
+}
+
+}  // namespace android
+}  // namespace dvr
+
+#endif  // ANDROID_DVR_ORTHO_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/pose.h b/libs/vr/libdvrcommon/include/private/dvr/pose.h
new file mode 100644
index 0000000..97944e8
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/pose.h
@@ -0,0 +1,118 @@
+#ifndef ANDROID_DVR_POSE_H_
+#define ANDROID_DVR_POSE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a 3D pose (rotation and position).
+//
+// @tparam T Data type for storing the position coordinate and rotation
+//     quaternion.
+template <typename T>
+class Pose {
+ public:
+  // Creates identity pose.
+  Pose()
+      : rotation_(Eigen::Quaternion<T>::Identity()),
+        position_(Eigen::Vector3<T>::Zero()) {}
+
+  // Initializes a pose with given rotation and position.
+  //
+  // rotation Initial rotation.
+  // position Initial position.
+  Pose(Eigen::Quaternion<T> rotation, Eigen::Vector3<T> position)
+      : rotation_(rotation), position_(position) {}
+
+  void Invert() {
+    rotation_ = rotation_.inverse();
+    position_ = rotation_ * -position_;
+  }
+
+  Pose Inverse() const {
+    Pose result(*this);
+    result.Invert();
+    return result;
+  }
+
+  // Compute the composition of this pose with another, storing the result
+  // in the current object
+  void ComposeInPlace(const Pose& other) {
+    position_ = position_ + rotation_ * other.position_;
+    rotation_ = rotation_ * other.rotation_;
+  }
+
+  // Computes the composition of this pose with another, and returns the result
+  Pose Compose(const Pose& other) const {
+    Pose result(*this);
+    result.ComposeInPlace(other);
+    return result;
+  }
+
+  Eigen::Vector3<T> TransformPoint(const Eigen::Vector3<T>& v) const {
+    return rotation_ * v + position_;
+  }
+
+  Eigen::Vector3<T> Transform(const Eigen::Vector3<T>& v) const {
+    return rotation_ * v;
+  }
+
+  Pose& operator*=(const Pose& other) {
+    ComposeInPlace(other);
+    return *this;
+  }
+
+  Pose operator*(const Pose& other) const { return Compose(other); }
+
+  // Gets the rotation of the 3D pose.
+  Eigen::Quaternion<T> GetRotation() const { return rotation_; }
+
+  // Gets the position of the 3D pose.
+  Eigen::Vector3<T> GetPosition() const { return position_; }
+
+  // Sets the rotation of the 3D pose.
+  void SetRotation(Eigen::Quaternion<T> rotation) { rotation_ = rotation; }
+
+  // Sets the position of the 3D pose.
+  void SetPosition(Eigen::Vector3<T> position) { position_ = position; }
+
+  // Gets a 4x4 matrix representing a transform from the reference space (that
+  // the rotation and position of the pose are relative to) to the object space.
+  Eigen::AffineMatrix<T, 4> GetObjectFromReferenceMatrix() const;
+
+  // Gets a 4x4 matrix representing a transform from the object space to the
+  // reference space (that the rotation and position of the pose are relative
+  // to).
+  Eigen::AffineMatrix<T, 4> GetReferenceFromObjectMatrix() const;
+
+ private:
+  Eigen::Quaternion<T> rotation_;
+  Eigen::Vector3<T> position_;
+};
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetObjectFromReferenceMatrix() const {
+  // The transfrom from the reference is the inverse of the pose.
+  Eigen::AffineMatrix<T, 4> matrix(rotation_.inverse().toRotationMatrix());
+  return matrix.translate(-position_);
+}
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetReferenceFromObjectMatrix() const {
+  // The transfrom to the reference.
+  Eigen::AffineMatrix<T, 4> matrix(rotation_.toRotationMatrix());
+  return matrix.pretranslate(position_);
+}
+
+//------------------------------------------------------------------------------
+// Type-specific typedefs.
+//------------------------------------------------------------------------------
+
+using Posef = Pose<float>;
+using Posed = Pose<double>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/range.h b/libs/vr/libdvrcommon/include/private/dvr/range.h
new file mode 100644
index 0000000..1d06c96
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/range.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_RANGE_H_
+#define ANDROID_DVR_RANGE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox
+
+// Container of two points that define a 2D range.
+template <class T, int d>
+struct Range {
+  // Construct an uninitialized Range.
+  Range() {}
+  Range(Eigen::Vector<T, d> p1, Eigen::Vector<T, d> p2) : p1(p1), p2(p2) {}
+
+  static Range<T, d> FromSize(Eigen::Vector<T, d> p1,
+                              Eigen::Vector<T, d> size) {
+    return Range<T, d>(p1, p1 + size);
+  }
+
+  bool operator==(const Range<T, d>& rhs) const {
+    return p1 == rhs.p1 && p2 == rhs.p2;
+  }
+
+  Eigen::Vector<T, d> GetMinPoint() const { return p1; }
+
+  Eigen::Vector<T, d> GetMaxPoint() const { return p2; }
+
+  Eigen::Vector<T, d> GetSize() const { return p2 - p1; }
+
+  Eigen::Vector<T, d> p1;
+  Eigen::Vector<T, d> p2;
+};
+
+typedef Range<int, 2> Range2i;
+typedef Range<float, 2> Range2f;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RANGE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
new file mode 100644
index 0000000..44485a7
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_RING_BUFFER_H_
+#define ANDROID_DVR_RING_BUFFER_H_
+
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A simple ring buffer implementation.
+//
+// A vector works but you either have to keep track of start_ and size_ yourself
+// or erase() from the front which is inefficient.
+//
+// A deque works but the common usage pattern of Append() PopFront() Append()
+// PopFront() looks like it allocates each time size goes from 0 --> 1, which we
+// don't want. This class allocates only once.
+template <typename T>
+class RingBuffer {
+ public:
+  RingBuffer() { Reset(0); }
+
+  explicit RingBuffer(size_t capacity) { Reset(capacity); }
+
+  RingBuffer(const RingBuffer& other) = default;
+  RingBuffer(RingBuffer&& other) = default;
+  RingBuffer& operator=(const RingBuffer& other) = default;
+  RingBuffer& operator=(RingBuffer&& other) = default;
+
+  void Append(const T& val) {
+    if (IsFull())
+      PopFront();
+    Get(size_) = val;
+    size_++;
+  }
+
+  void Append(T&& val) {
+    if (IsFull())
+      PopFront();
+    Get(size_) = std::move(val);
+    size_++;
+  }
+
+  bool IsEmpty() const { return size_ == 0; }
+
+  bool IsFull() const { return size_ == buffer_.size(); }
+
+  size_t GetSize() const { return size_; }
+
+  size_t GetCapacity() const { return buffer_.size(); }
+
+  T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; }
+
+  const T& Get(size_t i) const {
+    return buffer_[(start_ + i) % buffer_.size()];
+  }
+
+  const T& Back() const { return Get(size_ - 1); }
+
+  T& Back() { return Get(size_ - 1); }
+
+  const T& Front() const { return Get(0); }
+
+  T& Front() { return Get(0); }
+
+  void PopBack() {
+    if (size_ != 0) {
+      Get(size_ - 1) = T();
+      size_--;
+    }
+  }
+
+  void PopFront() {
+    if (size_ != 0) {
+      Get(0) = T();
+      start_ = (start_ + 1) % buffer_.size();
+      size_--;
+    }
+  }
+
+  void Clear() { Reset(GetCapacity()); }
+
+  void Reset(size_t capacity) {
+    start_ = size_ = 0;
+    buffer_.clear();
+    buffer_.resize(capacity);
+  }
+
+ private:
+  // Ideally we'd allocate our own memory and use placement new to instantiate
+  // instances of T instead of using a vector, but the vector is simpler.
+  std::vector<T> buffer_;
+  size_t start_, size_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RING_BUFFER_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
new file mode 100644
index 0000000..6048652
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
@@ -0,0 +1,124 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+
+#include <gtest/gtest.h>
+
+#include <cmath>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatEq(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int i = 0; i < N; ++i) {
+    if (!IsEqual(expected[i], actual[i], tolerance)) {
+      return ::testing::AssertionFailure()
+             << "\"" << expectedStr << "\" and \"" << actualStr
+             << "\" differ at element " << i << " by at least " << tolerance
+             << " : "
+             << " Expected \"" << expected[i] << "\", was \"" << actual[i]
+             << "\".";
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatEq(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int r = 0; r < N; ++r) {
+    for (int c = 0; c < N; ++c) {
+      if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+        return ::testing::AssertionFailure()
+               << "\"" << expectedStr << "\" and \"" << actualStr
+               << "\" differ at (" << r << "," << c << ")"
+               << " by at least " << tolerance << " : "
+               << " Expected \"" << expected(r, c) << "\", was \""
+               << actual(r, c) << "\".";
+      }
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatNe(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int i = 0; i < N; ++i) {
+    if (!IsEqual(expected[i], actual[i], tolerance)) {
+      return ::testing::AssertionSuccess();
+    }
+  }
+
+  ::testing::Message message;
+  message << "Expected \"" << expectedStr
+          << "\" to differ from provided value \"" << actualStr
+          << "\" by at least " << tolerance << ".";
+
+  return ::testing::AssertionFailure(message);
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatNe(
+    const char* expectedStr, const char* actualStr, const char* toleranceStr,
+    const A& expected, const B& actual, const T& tolerance) {
+  for (int r = 0; r < N; ++r) {
+    for (int c = 0; c < N; ++c) {
+      if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+        return ::testing::AssertionSuccess();
+      }
+    }
+  }
+
+  ::testing::Message message;
+  message << "Expected \"" << expectedStr
+          << "\" to differ from provided value \"" << actualStr
+          << "\" by at least " << tolerance << ".";
+
+  return ::testing::AssertionFailure(message);
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#define EXPECT_VEC3_NEAR(expected, actual, tol)                               \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \
+                      tol)
+
+#define EXPECT_VEC3_NOT_NEAR(expected, actual, tol)                           \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \
+                      tol)
+
+#define EXPECT_QUAT_NEAR(expected, actual, tol)                                \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \
+                      actual.coeffs(), tol)
+
+#define EXPECT_QUAT_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \
+                      actual.coeffs(), tol)
+
+#define EXPECT_MAT4_NEAR(expected, actual, tol)                                \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \
+                      tol)
+
+#define EXPECT_MAT4_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \
+                      tol)
+
+#define EXPECT_MAT3_NEAR(expected, actual, tol) \
+  EXPECT_PRED_FORMAT3(android::dvr              \
+                      : CmpMatrixLikeFloatEq<3>, expected, actual, tol)
+
+#define EXPECT_MAT3_NOT_NEAR(expected, actual, tol)                            \
+  EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \
+                      tol)
+
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/types.h b/libs/vr/libdvrcommon/include/private/dvr/types.h
new file mode 100644
index 0000000..1fa54af
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/types.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_TYPES_H_
+#define ANDROID_DVR_TYPES_H_
+
+// All basic types used by VR code.
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/range.h>
+
+namespace android {
+namespace dvr {
+
+enum RgbColorChannel { kRed, kGreen, kBlue };
+
+// EyeType: 0 for left, 1 for right.
+enum EyeType { kLeftEye = 0, kRightEye = 1 };
+
+// In the context of VR, vector types are used as much as base types.
+
+using vec2f = Eigen::Vector2f;
+using vec2d = Eigen::Vector2d;
+using vec2i = Eigen::Vector2i;
+using vec2 = vec2f;
+
+using vec3f = Eigen::Vector3f;
+using vec3d = Eigen::Vector3d;
+using vec3i = Eigen::Vector3i;
+using vec3 = vec3f;
+
+using vec4f = Eigen::Vector4f;
+using vec4d = Eigen::Vector4d;
+using vec4i = Eigen::Vector4i;
+using vec4 = vec4f;
+
+using mat3f = Eigen::AffineMatrix<float, 3>;
+using mat3d = Eigen::AffineMatrix<double, 3>;
+using mat3 = mat3f;
+
+using mat4f = Eigen::AffineMatrix<float, 4>;
+using mat4d = Eigen::AffineMatrix<double, 4>;
+using mat4 = mat4f;
+
+using quatf = Eigen::Quaternionf;
+using quatd = Eigen::Quaterniond;
+using quat = quatf;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_TYPES_H_
diff --git a/libs/vr/libdvrcommon/tests/numeric_test.cpp b/libs/vr/libdvrcommon/tests/numeric_test.cpp
new file mode 100644
index 0000000..1ee1447
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp
@@ -0,0 +1,67 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/numeric.h>
+
+using TestTypes = ::testing::Types<float, double, int>;
+
+using android::dvr::RandomInRange;
+
+template <typename T>
+class NumericTest : public ::testing::TestWithParam<T> {
+ public:
+  using FT = T;
+};
+
+TYPED_TEST_CASE(NumericTest, TestTypes);
+
+TYPED_TEST(NumericTest, RandomInRange) {
+  using FT = typename TestFixture::FT;
+
+  const int kNumTrials = 50;
+  const FT kLowRange = static_cast<FT>(-100);
+  const FT kHighRange = static_cast<FT>(100);
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    FT value = RandomInRange(kLowRange, kHighRange);
+
+    EXPECT_LE(kLowRange, value);
+    EXPECT_GE(kHighRange, value);
+  }
+}
+
+TEST(RandomInRange, TestIntVersion) {
+  // This checks specifically that the function does not always give the lo
+  // value (this was previously a bug)
+
+  const int kNumTrials = 50;
+  const int kLowRange = -100;
+  const int kHighRange = 100;
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    int value = RandomInRange(kLowRange, kHighRange);
+
+    if (value != kLowRange) {
+      SUCCEED();
+      return;
+    }
+  }
+
+  FAIL() << "Did not produce a value other than the range minimum for "
+         << "integers.";
+}
+
+TEST(RandomInRange, TestVectorVersion) {
+  Eigen::Vector3d lo(-3.0, -4.0, -5.0);
+  Eigen::Vector3d hi(5.0, 4.0, 3.0);
+
+  const int kNumTrials = 50;
+
+  for (int i = 0; i < kNumTrials; ++i) {
+    Eigen::Vector3d result = RandomInRange(lo, hi);
+
+    for (int j = 0; j < 3; ++j) {
+      EXPECT_LE(lo[j], result[j]);
+      EXPECT_GE(hi[j], result[j]);
+    }
+  }
+}
diff --git a/libs/vr/libdvrcommon/tests/pose_test.cpp b/libs/vr/libdvrcommon/tests/pose_test.cpp
new file mode 100644
index 0000000..aa1896d
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/pose_test.cpp
@@ -0,0 +1,154 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/test/test_macros.h>
+
+using PoseTypes = ::testing::Types<float, double>;
+
+template <class T>
+class PoseTest : public ::testing::TestWithParam<T> {
+ public:
+  using FT = T;
+  using Pose_t = android::dvr::Pose<FT>;
+  using quat_t = Eigen::Quaternion<FT>;
+  using vec3_t = Eigen::Vector3<FT>;
+  using mat4_t = Eigen::AffineMatrix<FT, 4>;
+};
+
+TYPED_TEST_CASE(PoseTest, PoseTypes);
+
+// Check that the two matrix methods are inverses of each other
+TYPED_TEST(PoseTest, SelfInverse) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using mat4_t = typename TestFixture::mat4_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t initial_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+  const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0));
+  const Pose_t initial_pose(initial_rotation, initial_position);
+
+  auto result_pose = initial_pose.GetReferenceFromObjectMatrix() *
+                     initial_pose.GetObjectFromReferenceMatrix();
+
+  EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance);
+}
+
+TYPED_TEST(PoseTest, TransformPoint) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(
+      Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+  const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0));
+
+  const Pose_t test_pose(pose_rotation, pose_position);
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_transformed =
+        (pose_rotation * start_position) + pose_position;
+    const vec3_t actual_transformed = test_pose.TransformPoint(start_position);
+    EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, TransformVector) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+
+  const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0));
+
+  const Pose_t test_pose(pose_rotation, pose_position);
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_rotated = pose_rotation * start_position;
+    const vec3_t actual_rotated = test_pose.Transform(start_position);
+    EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, Composition) {
+  using quat_t = typename TestFixture::quat_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t first_rotation(
+      Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+  const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0));
+  const quat_t second_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized()));
+  const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0));
+
+  const Pose_t first_pose(first_rotation, first_offset);
+  const Pose_t second_pose(second_rotation, second_offset);
+
+  const auto combined_pose(second_pose.Compose(first_pose));
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t expected_transformed =
+        second_pose.TransformPoint(first_pose.TransformPoint(start_position));
+    const vec3_t actual_transformed =
+        combined_pose.TransformPoint(start_position);
+    EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+  }
+}
+
+TYPED_TEST(PoseTest, Inverse) {
+  using quat_t = typename TestFixture::quat_t;
+  using vec3_t = typename TestFixture::vec3_t;
+  using Pose_t = typename TestFixture::Pose_t;
+  using FT = typename TestFixture::FT;
+
+  const auto tolerance = FT(0.0001);
+
+  const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+      FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized()));
+  const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0));
+
+  Pose_t pose(pose_rotation, pose_position);
+  const Pose_t pose_inverse = pose.Inverse();
+
+  for (int axis = 0; axis < 3; ++axis) {
+    vec3_t start_position = vec3_t::Zero();
+    start_position[axis] = FT(1.0);
+    const vec3_t transformed = pose.Transform(start_position);
+    const vec3_t inverted = pose_inverse.Transform(transformed);
+    EXPECT_VEC3_NEAR(start_position, inverted, tolerance);
+  }
+
+  Pose_t nullified_pose[2] = {
+      pose.Compose(pose_inverse), pose_inverse.Compose(pose),
+  };
+
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(),
+                     tolerance);
+    EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(),
+                     tolerance);
+  }
+}
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
new file mode 100644
index 0000000..f55e994
--- /dev/null
+++ b/libs/vr/libpdx/Android.bp
@@ -0,0 +1,60 @@
+cc_library_static {
+    name: "libpdx",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    export_include_dirs: ["private"],
+    local_include_dirs: ["private"],
+    srcs: [
+        "client.cpp",
+        "service.cpp",
+        "status.cpp",
+    ],
+}
+
+cc_test {
+    name: "pdx_tests",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "client_tests.cpp",
+        "mock_tests.cpp",
+        "serialization_tests.cpp",
+        "service_tests.cpp",
+        "status_tests.cpp",
+        "thread_local_buffer_tests.cpp",
+        "variant_tests.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libpdx",
+        "liblog",
+        "libutils",
+        "libvndksupport",
+    ],
+}
+
+// Code analysis target.
+cc_test {
+    name: "pdx_encoder_performance_test",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-O2",
+    ],
+    srcs: [
+        "encoder_performance_test.cpp",
+    ],
+    static_libs: [
+        "libpdx",
+    ],
+}
diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp
new file mode 100644
index 0000000..bfa2d87
--- /dev/null
+++ b/libs/vr/libpdx/client.cpp
@@ -0,0 +1,287 @@
+#include "pdx/client.h"
+
+#define LOG_TAG "ServiceFramework"
+#include <log/log.h>
+
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+
+void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) {
+  if (channel_factory_) {
+    reconnect_timeout_ms_ = reconnect_timeout_ms;
+    auto_reconnect_enabled_ = true;
+  }
+}
+
+void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; }
+
+bool Client::IsConnected() const { return channel_.get() != nullptr; }
+
+Status<void> Client::CheckReconnect() {
+  Status<void> ret;
+  bool was_disconnected = !IsConnected();
+  if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) {
+    auto status = channel_factory_->Connect(reconnect_timeout_ms_);
+    if (!status) {
+      error_ = -status.error();
+      ret.SetError(status.error());
+      return ret;
+    }
+    channel_ = status.take();
+  }
+
+  if (!IsConnected()) {
+    ret.SetError(ESHUTDOWN);
+  } else {
+    // Call the subclass OnConnect handler. The subclass may choose to close the
+    // connection in the handler, in which case error_ will be non-zero.
+    if (was_disconnected)
+      OnConnect();
+    if (!IsConnected())
+      ret.SetError(-error_);
+    else
+      ret.SetValue();
+  }
+
+  return ret;
+}
+
+bool Client::NeedToDisconnectChannel(int error) const {
+  return error == ESHUTDOWN && auto_reconnect_enabled_;
+}
+
+void Client::CheckDisconnect(int error) {
+  if (NeedToDisconnectChannel(error))
+    Close(error);
+}
+
+Client::Client(std::unique_ptr<ClientChannel> channel)
+    : channel_{std::move(channel)} {}
+
+Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+               int64_t timeout_ms)
+    : channel_factory_{std::move(channel_factory)} {
+  auto status = channel_factory_->Connect(timeout_ms);
+  if (!status) {
+    ALOGE("Client::Client: Failed to connect to service because: %s",
+          status.GetErrorMessage().c_str());
+    error_ = -status.error();
+  } else {
+    channel_ = status.take();
+  }
+}
+
+bool Client::IsInitialized() const {
+  return IsConnected() || (channel_factory_ && auto_reconnect_enabled_);
+}
+
+void Client::OnConnect() {}
+
+int Client::error() const { return error_; }
+
+Status<void> Client::SendImpulse(int opcode) {
+  PDX_TRACE_NAME("Client::SendImpulse");
+
+  auto status = CheckReconnect();
+  if (!status)
+    return status;
+
+  status = channel_->SendImpulse(opcode, nullptr, 0);
+  CheckDisconnect(status);
+  return status;
+}
+
+Status<void> Client::SendImpulse(int opcode, const void* buffer,
+                                 size_t length) {
+  PDX_TRACE_NAME("Client::SendImpulse");
+
+  auto status = CheckReconnect();
+  if (!status)
+    return status;
+
+  status = channel_->SendImpulse(opcode, buffer, length);
+  CheckDisconnect(status);
+  return status;
+}
+
+void Client::Close(int error) {
+  channel_.reset();
+  // Normalize error codes to negative integer space.
+  error_ = error <= 0 ? error : -error;
+}
+
+int Client::event_fd() const {
+  return IsConnected() ? channel_->event_fd() : -1;
+}
+
+LocalChannelHandle& Client::GetChannelHandle() {
+  return channel_->GetChannelHandle();
+}
+
+///////////////////////////// Transaction implementation //////////////////////
+
+Transaction::Transaction(Client& client) : client_{client} {}
+
+Transaction::~Transaction() {
+  if (state_allocated_ && client_.GetChannel())
+    client_.GetChannel()->FreeTransactionState(state_);
+}
+
+bool Transaction::EnsureStateAllocated() {
+  if (!state_allocated_ && client_.GetChannel()) {
+    state_ = client_.GetChannel()->AllocateTransactionState();
+    state_allocated_ = true;
+  }
+  return state_allocated_;
+}
+
+void Transaction::SendTransaction(int opcode, Status<void>* ret,
+                                  const iovec* send_vector, size_t send_count,
+                                  const iovec* receive_vector,
+                                  size_t receive_count) {
+  *ret = client_.CheckReconnect();
+  if (!*ret)
+    return;
+
+  if (!EnsureStateAllocated()) {
+    ret->SetError(ESHUTDOWN);
+    return;
+  }
+
+  auto status = client_.GetChannel()->SendWithInt(
+      state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+  if (status) {
+    ret->SetValue();
+  } else {
+    ret->SetError(status.error());
+  }
+  CheckDisconnect(status);
+}
+
+void Transaction::SendTransaction(int opcode, Status<int>* ret,
+                                  const iovec* send_vector, size_t send_count,
+                                  const iovec* receive_vector,
+                                  size_t receive_count) {
+  auto status = client_.CheckReconnect();
+  if (!status) {
+    ret->SetError(status.error());
+    return;
+  }
+
+  if (!EnsureStateAllocated()) {
+    ret->SetError(ESHUTDOWN);
+    return;
+  }
+
+  *ret = client_.GetChannel()->SendWithInt(
+      state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+  CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret,
+                                  const iovec* send_vector, size_t send_count,
+                                  const iovec* receive_vector,
+                                  size_t receive_count) {
+  auto status = client_.CheckReconnect();
+  if (!status) {
+    ret->SetError(status.error());
+    return;
+  }
+
+  if (!EnsureStateAllocated()) {
+    ret->SetError(ESHUTDOWN);
+    return;
+  }
+
+  *ret = client_.GetChannel()->SendWithFileHandle(
+      state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+  CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+                                  const iovec* send_vector, size_t send_count,
+                                  const iovec* receive_vector,
+                                  size_t receive_count) {
+  auto status = client_.CheckReconnect();
+  if (!status) {
+    ret->SetError(status.error());
+    return;
+  }
+
+  if (!EnsureStateAllocated()) {
+    ret->SetError(ESHUTDOWN);
+    return;
+  }
+
+  *ret = client_.GetChannel()->SendWithChannelHandle(
+      state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+  CheckDisconnect(*ret);
+}
+
+Status<FileReference> Transaction::PushFileHandle(const LocalHandle& handle) {
+  if (client_.CheckReconnect() && EnsureStateAllocated())
+    return client_.GetChannel()->PushFileHandle(state_, handle);
+  return ErrorStatus{ESHUTDOWN};
+}
+
+Status<FileReference> Transaction::PushFileHandle(
+    const BorrowedHandle& handle) {
+  if (client_.CheckReconnect() && EnsureStateAllocated())
+    return client_.GetChannel()->PushFileHandle(state_, handle);
+  return ErrorStatus{ESHUTDOWN};
+}
+
+Status<FileReference> Transaction::PushFileHandle(const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+    const LocalChannelHandle& handle) {
+  if (client_.CheckReconnect() && EnsureStateAllocated())
+    return client_.GetChannel()->PushChannelHandle(state_, handle);
+  return ErrorStatus{ESHUTDOWN};
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+    const BorrowedChannelHandle& handle) {
+  if (client_.CheckReconnect() && EnsureStateAllocated())
+    return client_.GetChannel()->PushChannelHandle(state_, handle);
+  return ErrorStatus{ESHUTDOWN};
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+    const RemoteChannelHandle& handle) {
+  return handle.value();
+}
+
+bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated() &&
+         client_.GetChannel()->GetFileHandle(state_, ref, handle);
+}
+
+bool Transaction::GetChannelHandle(ChannelReference ref,
+                                   LocalChannelHandle* handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated() &&
+         client_.GetChannel()->GetChannelHandle(state_, ref, handle);
+}
+
+void Transaction::CheckDisconnect(int error) {
+  if (client_.NeedToDisconnectChannel(error)) {
+    if (state_allocated_) {
+      if (client_.GetChannel())
+        client_.GetChannel()->FreeTransactionState(state_);
+      state_ = nullptr;
+      state_allocated_ = false;
+    }
+    client_.Close(error);
+  }
+}
+
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx/client_tests.cpp b/libs/vr/libpdx/client_tests.cpp
new file mode 100644
index 0000000..99ccc69
--- /dev/null
+++ b/libs/vr/libpdx/client_tests.cpp
@@ -0,0 +1,567 @@
+#include <pdx/client.h>
+
+#include <gmock/gmock.h>
+#include <sys/eventfd.h>
+
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ClientBase;
+using android::pdx::ClientChannel;
+using android::pdx::ClientChannelFactory;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::MockClientChannel;
+using android::pdx::MockClientChannelFactory;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::Void;
+
+using testing::A;
+using testing::AnyNumber;
+using testing::ByMove;
+using testing::Invoke;
+using testing::Ne;
+using testing::Return;
+using testing::_;
+
+namespace {
+
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+inline const void* IntToConstPtr(intptr_t addr) {
+  return reinterpret_cast<const void*>(addr);
+}
+
+struct TestInterface final {
+  // Op codes.
+  enum {
+    kOpAdd = 0,
+    kOpSendFile,
+    kOpGetFile,
+    kOpPushChannel,
+  };
+
+  // Methods.
+  PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+  PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd));
+  PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+  PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+
+  PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel);
+};
+
+class SimpleClient : public ClientBase<SimpleClient> {
+ public:
+  explicit SimpleClient(std::unique_ptr<ClientChannel> channel)
+      : BASE{std::move(channel)} {}
+  SimpleClient(std::unique_ptr<ClientChannelFactory> channel_factory,
+               int64_t timeout_ms)
+      : BASE{std::move(channel_factory), timeout_ms} {
+    EnableAutoReconnect(timeout_ms);
+  }
+
+  using BASE::SendImpulse;
+  using BASE::InvokeRemoteMethod;
+  using BASE::InvokeRemoteMethodInPlace;
+  using BASE::Close;
+  using BASE::IsConnected;
+  using BASE::EnableAutoReconnect;
+  using BASE::DisableAutoReconnect;
+  using BASE::event_fd;
+  using BASE::GetChannel;
+
+  MOCK_METHOD0(OnConnect, void());
+};
+
+class FailingClient : public ClientBase<FailingClient> {
+ public:
+  explicit FailingClient(std::unique_ptr<ClientChannel> channel, int error_code)
+      : BASE{std::move(channel)} {
+    Close(error_code);
+  }
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+  ClientChannelTest()
+      : client_{SimpleClient::Create(
+            std::make_unique<testing::StrictMock<MockClientChannel>>())} {}
+
+  MockClientChannel* mock_channel() {
+    return static_cast<MockClientChannel*>(client_->GetChannel());
+  }
+
+  std::unique_ptr<SimpleClient> client_;
+};
+
+class ClientChannelFactoryTest : public testing::Test {
+ public:
+  ClientChannelFactoryTest() {
+    auto factory =
+        std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+    ON_CALL(*factory, Connect(kTimeout))
+        .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+    client_ = SimpleClient::Create(std::move(factory), kTimeout);
+  }
+
+  MockClientChannel* mock_channel() {
+    return static_cast<MockClientChannel*>(client_->GetChannel());
+  }
+
+  Status<std::unique_ptr<ClientChannel>> OnConnect(int64_t /*timeout_ms*/) {
+    if (on_connect_error_)
+      return ErrorStatus(on_connect_error_);
+    std::unique_ptr<MockClientChannel> channel =
+        std::make_unique<testing::StrictMock<MockClientChannel>>();
+    if (on_connect_callback_)
+      on_connect_callback_(channel.get());
+    return Status<std::unique_ptr<ClientChannel>>{std::move(channel)};
+  }
+
+  void OnConnectCallback(std::function<void(MockClientChannel*)> callback) {
+    on_connect_callback_ = callback;
+  }
+  void SetOnConnectError(int error) { on_connect_error_ = error; }
+  void ResetOnConnectError() { on_connect_error_ = 0; }
+
+  constexpr static int64_t kTimeout = 123;
+  std::unique_ptr<SimpleClient> client_;
+  std::function<void(MockClientChannel*)> on_connect_callback_;
+  int on_connect_error_{0};
+};
+
+constexpr int64_t ClientChannelFactoryTest::kTimeout;
+
+class ClientTransactionTest : public ClientChannelTest {
+ public:
+  ClientTransactionTest() : transaction_{*client_} {}
+
+  Transaction transaction_;
+};
+
+}  // anonymous namespace
+
+TEST_F(ClientChannelTest, IsInitialized) {
+  ASSERT_NE(client_.get(), nullptr);
+  EXPECT_TRUE(client_->IsInitialized());
+  EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelTest, CloseOnConstruction) {
+  FailingClient failed_client1{std::make_unique<MockClientChannel>(), EACCES};
+  ASSERT_FALSE(failed_client1.IsInitialized());
+  EXPECT_EQ(-EACCES, failed_client1.error());
+
+  FailingClient failed_client2{std::make_unique<MockClientChannel>(), -EACCES};
+  ASSERT_FALSE(failed_client2.IsInitialized());
+  EXPECT_EQ(-EACCES, failed_client2.error());
+
+  auto failed_client3 =
+      FailingClient::Create(std::make_unique<MockClientChannel>(), EIO);
+  ASSERT_EQ(failed_client3.get(), nullptr);
+}
+
+TEST_F(ClientChannelTest, IsConnected) {
+  EXPECT_TRUE(client_->IsConnected());
+  EXPECT_EQ(0, client_->error());
+  client_->Close(-EINVAL);
+  EXPECT_FALSE(client_->IsConnected());
+  EXPECT_EQ(-EINVAL, client_->error());
+}
+
+TEST_F(ClientChannelTest, event_fd) {
+  EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12));
+  EXPECT_EQ(12, client_->event_fd());
+}
+
+TEST_F(ClientChannelTest, SendImpulse) {
+  EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(client_->SendImpulse(123));
+
+  EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  auto status = client_->SendImpulse(17);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+
+  const void* const kTestPtr = IntToConstPtr(1234);
+  EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) {
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(nullptr));
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0))
+      .WillOnce(Return(9));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+  EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::Add>(4, 5));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(
+      *mock_channel(),
+      SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+      .WillOnce(Return(3));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+  ASSERT_TRUE(status);
+  EXPECT_EQ(3, status.get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(
+      *mock_channel(),
+      SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) {
+  void* const kTransactionState = IntToPtr(123);
+  int fd = eventfd(0, 0);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(),
+              SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+                                 _, _, nullptr, 0))
+      .WillOnce(Return(ByMove(LocalHandle{fd})));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<LocalHandle> status =
+      client_->InvokeRemoteMethod<TestInterface::GetFile>();
+  ASSERT_TRUE(status);
+  EXPECT_EQ(fd, status.get().Get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(),
+              SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+                                 _, _, nullptr, 0))
+      .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<LocalHandle> status =
+      client_->InvokeRemoteMethod<TestInterface::GetFile>("file", 0);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) {
+  void* const kTransactionState = IntToPtr(123);
+  const int32_t kHandleValue = 17;
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(
+      *mock_channel(),
+      SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+                            _, nullptr, 0))
+      .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue})));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<LocalChannelHandle> status =
+      client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+  ASSERT_TRUE(status);
+  EXPECT_EQ(kHandleValue, status.get().value());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(
+      *mock_channel(),
+      SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+                            _, nullptr, 0))
+      .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  Status<LocalChannelHandle> status =
+      client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) {
+  void* const kTransactionState = IntToPtr(123);
+  LocalHandle fd{eventfd(0, 0)};
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(),
+              PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+      .WillOnce(Return(1));
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+                          nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) {
+  void* const kTransactionState = IntToPtr(123);
+  LocalHandle fd{eventfd(0, 0)};
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(),
+              PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+      .WillOnce(Return(1));
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+                          nullptr, 0))
+      .WillOnce(Return(ErrorStatus{EACCES}));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  EXPECT_FALSE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelFactoryTest, IsInitialized) {
+  ASSERT_NE(client_.get(), nullptr);
+  EXPECT_TRUE(client_->IsInitialized());
+  EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) {
+  auto factory =
+      std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+  EXPECT_CALL(*factory, Connect(kTimeout))
+      .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN))))
+      .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+  client_ = SimpleClient::Create(std::move(factory), kTimeout);
+  ASSERT_NE(client_.get(), nullptr);
+  EXPECT_TRUE(client_->IsInitialized());
+  EXPECT_FALSE(client_->IsConnected());
+  client_->DisableAutoReconnect();
+  ASSERT_FALSE(client_->SendImpulse(17));
+  EXPECT_FALSE(client_->IsConnected());
+  client_->EnableAutoReconnect(kTimeout);
+  EXPECT_CALL(*client_, OnConnect());
+  OnConnectCallback([](auto* mock) {
+    EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+        .WillOnce(Return(Status<void>{}));
+  });
+  ASSERT_TRUE(client_->SendImpulse(17));
+  EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckDisconnect) {
+  EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+      .WillOnce(Return(ErrorStatus{ESHUTDOWN}));
+  ASSERT_FALSE(client_->SendImpulse(17));
+  EXPECT_FALSE(client_->IsConnected());
+  EXPECT_EQ(-ESHUTDOWN, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckReconnect) {
+  client_->Close(ESHUTDOWN);
+  ASSERT_FALSE(client_->IsConnected());
+
+  EXPECT_CALL(*client_, OnConnect());
+  OnConnectCallback([](auto* mock) {
+    EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+        .WillOnce(Return(Status<void>{}));
+  });
+  ASSERT_TRUE(client_->SendImpulse(17));
+  EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CloseOnConnect) {
+  client_->Close(ESHUTDOWN);
+
+  EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] {
+    client_->Close(EIO);
+  }));
+  auto status = client_->SendImpulse(17);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+  EXPECT_FALSE(client_->IsConnected());
+  EXPECT_EQ(-EIO, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) {
+  client_->Close(EIO);
+  ASSERT_FALSE(client_->IsConnected());
+  client_->DisableAutoReconnect();
+  auto status = client_->SendImpulse(17);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(ESHUTDOWN, status.error());
+  EXPECT_FALSE(client_->IsConnected());
+  client_->EnableAutoReconnect(kTimeout);
+  EXPECT_CALL(*client_, OnConnect());
+  OnConnectCallback([](auto* mock) {
+    EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+        .WillOnce(Return(Status<void>{}));
+  });
+  ASSERT_TRUE(client_->SendImpulse(17));
+  EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientTransactionTest, SendNoData) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(1));
+  EXPECT_CALL(*mock_channel(),
+              SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(ByMove(LocalHandle{-1})));
+  EXPECT_TRUE(transaction_.Send<LocalHandle>(2));
+  EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3,
+                                                     nullptr, 0, nullptr, 0))
+      .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1})));
+  EXPECT_TRUE(transaction_.Send<LocalChannelHandle>(3));
+}
+
+TEST_F(ClientTransactionTest, SendNoState) {
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(nullptr));
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+  EXPECT_TRUE(transaction_.Send<void>(1));
+}
+
+TEST_F(ClientTransactionTest, SendBuffers) {
+  const void* const kSendBuffer = IntToConstPtr(123);
+  const size_t kSendSize = 12;
+  void* const kReceiveBuffer = IntToPtr(456);
+  const size_t kReceiveSize = 34;
+
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(nullptr));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(1, nullptr, 0, nullptr, 0));
+
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(2, kSendBuffer, kSendSize, nullptr, 0));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(3, kSendBuffer, 0, nullptr, 0));
+
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(
+      transaction_.Send<void>(4, nullptr, 0, kReceiveBuffer, kReceiveSize));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(5, nullptr, 0, kReceiveBuffer, 0));
+
+  EXPECT_CALL(*mock_channel(),
+              SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.Send<void>(5, kSendBuffer, kSendSize, kReceiveBuffer,
+                                      kReceiveSize));
+}
+
+TEST_F(ClientTransactionTest, SendVector) {
+  iovec send[3] = {};
+  iovec recv[4] = {};
+
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(nullptr));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.SendVector<void>(1, nullptr, 0, nullptr, 0));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.SendVector<void>(2, send, 3, recv, 4));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.SendVector<void>(3, send, nullptr));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.SendVector<void>(4, nullptr, recv));
+
+  EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4))
+      .WillOnce(Return(0));
+  EXPECT_TRUE(transaction_.SendVector<void>(5, send, recv));
+}
+
+TEST_F(ClientTransactionTest, PushHandle) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+  EXPECT_CALL(*mock_channel(),
+              PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+      .WillOnce(Return(1));
+  EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}).get());
+
+  EXPECT_CALL(*mock_channel(),
+              PushFileHandle(kTransactionState, A<const BorrowedHandle&>()))
+      .WillOnce(Return(2));
+  EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}).get());
+
+  EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}).get());
+
+  EXPECT_CALL(
+      *mock_channel(),
+      PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>()))
+      .WillOnce(Return(11));
+  EXPECT_EQ(
+      11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}).get());
+
+  EXPECT_CALL(
+      *mock_channel(),
+      PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>()))
+      .WillOnce(Return(12));
+  EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}).get());
+
+  EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}).get());
+}
+
+TEST_F(ClientTransactionTest, GetHandle) {
+  void* const kTransactionState = IntToPtr(123);
+  EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+      .WillOnce(Return(kTransactionState));
+  EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+  EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true));
+
+  LocalHandle file_handle;
+  EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle));
+  EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle));
+
+  EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true));
+
+  LocalChannelHandle channel_handle;
+  EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle));
+  EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle));
+}
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
new file mode 100644
index 0000000..b7d94b3
--- /dev/null
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -0,0 +1,515 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx::rpc;
+using namespace android::pdx;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+using std::placeholders::_6;
+
+namespace {
+
+constexpr size_t kMaxStaticBufferSize = 20480;
+
+// Provide numpunct facet that formats numbers with ',' as thousands separators.
+class CommaNumPunct : public std::numpunct<char> {
+ protected:
+  char do_thousands_sep() const override { return ','; }
+  std::string do_grouping() const override { return "\03"; }
+};
+
+class TestPayload : public MessagePayload<SendBuffer>,
+                    public MessageWriter,
+                    public MessageReader,
+                    public NoOpResourceMapper {
+ public:
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override {
+    const size_t section_offset = Size();
+    Extend(size);
+    return Data() + section_offset;
+  }
+
+  OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override {
+    return {&*ConstCursor(), &*ConstEnd()};
+  }
+
+  void ConsumeReadBufferSectionData(const void* new_start) override {
+    std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor()));
+  }
+
+  InputResourceMapper* GetInputResourceMapper() override { return this; }
+};
+
+class StaticBuffer : public MessageWriter,
+                     public MessageReader,
+                     public NoOpResourceMapper {
+ public:
+  void Clear() {
+    read_ptr_ = buffer_;
+    write_ptr_ = 0;
+  }
+  void Rewind() { read_ptr_ = buffer_; }
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override {
+    void* ptr = buffer_ + write_ptr_;
+    write_ptr_ += size;
+    return ptr;
+  }
+
+  OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override {
+    return {read_ptr_, std::end(buffer_)};
+  }
+
+  void ConsumeReadBufferSectionData(const void* new_start) override {
+    read_ptr_ = static_cast<const uint8_t*>(new_start);
+  }
+
+  InputResourceMapper* GetInputResourceMapper() override { return this; }
+
+ private:
+  uint8_t buffer_[kMaxStaticBufferSize];
+  const uint8_t* read_ptr_{buffer_};
+  size_t write_ptr_{0};
+};
+
+// Simple callback function to clear/reset the input/output buffers for
+// serialization. Using raw function pointer here instead of std::function to
+// minimize the overhead of invocation in the tight test loop over millions of
+// iterations.
+using ResetFunc = void(void*);
+
+// Serialization test function signature, used by the TestRunner.
+using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer,
+                                                        size_t iterations,
+                                                        ResetFunc* write_reset,
+                                                        void* reset_data);
+
+// Deserialization test function signature, used by the TestRunner.
+using DeserializeTestSignature = std::chrono::nanoseconds(
+    MessageReader* reader, MessageWriter* writer, size_t iterations,
+    ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data);
+
+// Generic serialization test runner method. Takes the |value| of type T and
+// serializes it into the output buffer represented by |writer|.
+template <typename T>
+std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer,
+                                             size_t iterations,
+                                             ResetFunc* write_reset,
+                                             void* reset_data, const T& value) {
+  auto start = std::chrono::high_resolution_clock::now();
+  for (size_t i = 0; i < iterations; i++) {
+    write_reset(reset_data);
+    Serialize(value, writer);
+  }
+  auto stop = std::chrono::high_resolution_clock::now();
+  return stop - start;
+}
+
+// Generic deserialization test runner method. Takes the |value| of type T and
+// temporarily serializes it into the output buffer, then repeatedly
+// deserializes the data back from that buffer.
+template <typename T>
+std::chrono::nanoseconds DeserializeTestRunner(
+    MessageReader* reader, MessageWriter* writer, size_t iterations,
+    ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+    const T& value) {
+  write_reset(reset_data);
+  Serialize(value, writer);
+  T output_data;
+  auto start = std::chrono::high_resolution_clock::now();
+  for (size_t i = 0; i < iterations; i++) {
+    read_reset(reset_data);
+    Deserialize(&output_data, reader);
+  }
+  auto stop = std::chrono::high_resolution_clock::now();
+  if (output_data != value)
+    return start - stop;  // Return negative value to indicate error.
+  return stop - start;
+}
+
+// Special version of SerializeTestRunner that doesn't perform any serialization
+// but does all the same setup steps and moves data of size |data_size| into
+// the output buffer. Useful to determine the baseline to calculate time used
+// just for serialization layer.
+std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer,
+                                           size_t iterations,
+                                           ResetFunc* write_reset,
+                                           void* reset_data, size_t data_size) {
+  std::vector<uint8_t> dummy_data(data_size);
+  auto start = std::chrono::high_resolution_clock::now();
+  for (size_t i = 0; i < iterations; i++) {
+    write_reset(reset_data);
+    memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+           dummy_data.data(), dummy_data.size());
+  }
+  auto stop = std::chrono::high_resolution_clock::now();
+  return stop - start;
+}
+
+// Special version of DeserializeTestRunner that doesn't perform any
+// deserialization but invokes Rewind on the input buffer repeatedly.
+// Useful to determine the baseline to calculate time used just for
+// deserialization layer.
+std::chrono::nanoseconds DeserializeBaseTest(
+    MessageReader* reader, MessageWriter* writer, size_t iterations,
+    ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+    size_t data_size) {
+  std::vector<uint8_t> dummy_data(data_size);
+  write_reset(reset_data);
+  memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+         dummy_data.data(), dummy_data.size());
+  auto start = std::chrono::high_resolution_clock::now();
+  for (size_t i = 0; i < iterations; i++) {
+    read_reset(reset_data);
+    auto section = reader->GetNextReadBufferSection();
+    memcpy(dummy_data.data(), section.first, dummy_data.size());
+    reader->ConsumeReadBufferSectionData(
+        AdvancePointer(section.first, dummy_data.size()));
+  }
+  auto stop = std::chrono::high_resolution_clock::now();
+  return stop - start;
+}
+
+// The main class that accumulates individual tests to be executed.
+class TestRunner {
+ public:
+  struct BufferInfo {
+    BufferInfo(const std::string& buffer_name, MessageReader* reader,
+               MessageWriter* writer, ResetFunc* read_reset_func,
+               ResetFunc* write_reset_func, void* reset_data)
+        : name{buffer_name},
+          reader{reader},
+          writer{writer},
+          read_reset_func{read_reset_func},
+          write_reset_func{write_reset_func},
+          reset_data{reset_data} {}
+    std::string name;
+    MessageReader* reader;
+    MessageWriter* writer;
+    ResetFunc* read_reset_func;
+    ResetFunc* write_reset_func;
+    void* reset_data;
+  };
+
+  void AddTestFunc(const std::string& name,
+                   std::function<SerializeTestSignature> serialize_test,
+                   std::function<DeserializeTestSignature> deserialize_test,
+                   size_t data_size) {
+    tests_.emplace_back(name, std::move(serialize_test),
+                        std::move(deserialize_test), data_size);
+  }
+
+  template <typename T>
+  void AddSerializationTest(const std::string& name, T&& value) {
+    const size_t data_size = GetSerializedSize(value);
+    auto serialize_test =
+        std::bind(static_cast<std::chrono::nanoseconds (*)(
+                      MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+                      &SerializeTestRunner),
+                  _1, _2, _3, _4, std::forward<T>(value));
+    tests_.emplace_back(name, std::move(serialize_test),
+                        std::function<DeserializeTestSignature>{}, data_size);
+  }
+
+  template <typename T>
+  void AddDeserializationTest(const std::string& name, T&& value) {
+    const size_t data_size = GetSerializedSize(value);
+    auto deserialize_test =
+        std::bind(static_cast<std::chrono::nanoseconds (*)(
+                      MessageReader*, MessageWriter*, size_t, ResetFunc*,
+                      ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+                  _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+    tests_.emplace_back(name, std::function<SerializeTestSignature>{},
+                        std::move(deserialize_test), data_size);
+  }
+
+  template <typename T>
+  void AddTest(const std::string& name, T&& value) {
+    const size_t data_size = GetSerializedSize(value);
+    if (data_size > kMaxStaticBufferSize) {
+      std::cerr << "Test '" << name << "' requires " << data_size
+                << " bytes in the serialization buffer but only "
+                << kMaxStaticBufferSize << " are available." << std::endl;
+      exit(1);
+    }
+    auto serialize_test =
+        std::bind(static_cast<std::chrono::nanoseconds (*)(
+                      MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+                      &SerializeTestRunner),
+                  _1, _2, _3, _4, value);
+    auto deserialize_test =
+        std::bind(static_cast<std::chrono::nanoseconds (*)(
+                      MessageReader*, MessageWriter*, size_t, ResetFunc*,
+                      ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+                  _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+    tests_.emplace_back(name, std::move(serialize_test),
+                        std::move(deserialize_test), data_size);
+  }
+
+  std::string CenterString(std::string text, size_t column_width) {
+    if (text.size() < column_width) {
+      text = std::string((column_width - text.size()) / 2, ' ') + text;
+    }
+    return text;
+  }
+
+  void RunTests(size_t iteration_count,
+                const std::vector<BufferInfo>& buffers) {
+    using float_seconds = std::chrono::duration<double>;
+    const std::string name_column_separator = " : ";
+    const std::string buffer_column_separator = " || ";
+    const std::string buffer_timing_column_separator = " | ";
+    const size_t data_size_column_width = 6;
+    const size_t time_column_width = 9;
+    const size_t qps_column_width = 18;
+    const size_t buffer_column_width = time_column_width +
+                                       buffer_timing_column_separator.size() +
+                                       qps_column_width;
+
+    auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) {
+      return t1.name.size() < t2.name.size();
+    };
+    auto test_with_longest_name =
+        std::max_element(tests_.begin(), tests_.end(), compare_name_length);
+    size_t name_column_width = test_with_longest_name->name.size();
+
+    size_t total_width =
+        name_column_width + name_column_separator.size() +
+        data_size_column_width + buffer_column_separator.size() +
+        buffers.size() * (buffer_column_width + buffer_column_separator.size());
+
+    const std::string dbl_separator(total_width, '=');
+    const std::string separator(total_width, '-');
+
+    auto print_header = [&](const std::string& header) {
+      std::cout << dbl_separator << std::endl;
+      std::stringstream ss;
+      ss.imbue(std::locale(ss.getloc(), new CommaNumPunct));
+      ss << header << " (" << iteration_count << " iterations)";
+      std::cout << CenterString(ss.str(), total_width) << std::endl;
+      std::cout << dbl_separator << std::endl;
+      std::cout << std::setw(name_column_width) << "Test Name" << std::left
+                << name_column_separator << std::setw(data_size_column_width)
+                << CenterString("Size", data_size_column_width)
+                << buffer_column_separator;
+      for (const auto& buffer_info : buffers) {
+        std::cout << std::setw(buffer_column_width)
+                  << CenterString(buffer_info.name, buffer_column_width)
+                  << buffer_column_separator;
+      }
+      std::cout << std::endl;
+      std::cout << std::setw(name_column_width) << "" << name_column_separator
+                << std::setw(data_size_column_width)
+                << CenterString("bytes", data_size_column_width)
+                << buffer_column_separator << std::left;
+      for (size_t i = 0; i < buffers.size(); i++) {
+        std::cout << std::setw(time_column_width)
+                  << CenterString("Time, s", time_column_width)
+                  << buffer_timing_column_separator
+                  << std::setw(qps_column_width)
+                  << CenterString("QPS", qps_column_width)
+                  << buffer_column_separator;
+      }
+      std::cout << std::right << std::endl;
+      std::cout << separator << std::endl;
+    };
+
+    print_header("Serialization benchmarks");
+    for (const auto& test : tests_) {
+      if (test.serialize_test) {
+        std::cout << std::setw(name_column_width) << test.name << " : "
+                  << std::setw(data_size_column_width) << test.data_size
+                  << buffer_column_separator;
+        for (const auto& buffer_info : buffers) {
+          auto seconds =
+              std::chrono::duration_cast<float_seconds>(test.serialize_test(
+                  buffer_info.writer, iteration_count,
+                  buffer_info.write_reset_func, buffer_info.reset_data));
+          double qps = iteration_count / seconds.count();
+          std::cout << std::fixed << std::setprecision(3)
+                    << std::setw(time_column_width) << seconds.count()
+                    << buffer_timing_column_separator
+                    << std::setw(qps_column_width) << qps
+                    << buffer_column_separator;
+        }
+        std::cout << std::endl;
+      }
+    }
+
+    print_header("Deserialization benchmarks");
+    for (const auto& test : tests_) {
+      if (test.deserialize_test) {
+        std::cout << std::setw(name_column_width) << test.name << " : "
+                  << std::setw(data_size_column_width) << test.data_size
+                  << buffer_column_separator;
+        for (const auto& buffer_info : buffers) {
+          auto seconds =
+              std::chrono::duration_cast<float_seconds>(test.deserialize_test(
+                  buffer_info.reader, buffer_info.writer, iteration_count,
+                  buffer_info.read_reset_func, buffer_info.write_reset_func,
+                  buffer_info.reset_data));
+          double qps = iteration_count / seconds.count();
+          std::cout << std::fixed << std::setprecision(3)
+                    << std::setw(time_column_width) << seconds.count()
+                    << buffer_timing_column_separator
+                    << std::setw(qps_column_width) << qps
+                    << buffer_column_separator;
+        }
+        std::cout << std::endl;
+      }
+    }
+    std::cout << dbl_separator << std::endl;
+  }
+
+ private:
+  struct TestEntry {
+    TestEntry(const std::string& test_name,
+              std::function<SerializeTestSignature> serialize_test,
+              std::function<DeserializeTestSignature> deserialize_test,
+              size_t data_size)
+        : name{test_name},
+          serialize_test{std::move(serialize_test)},
+          deserialize_test{std::move(deserialize_test)},
+          data_size{data_size} {}
+    std::string name;
+    std::function<SerializeTestSignature> serialize_test;
+    std::function<DeserializeTestSignature> deserialize_test;
+    size_t data_size;
+  };
+
+  std::vector<TestEntry> tests_;
+};
+
+std::string GenerateContainerName(const std::string& type, size_t count) {
+  std::stringstream ss;
+  ss << type << "(" << count << ")";
+  return ss.str();
+}
+
+}  // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+  const size_t iteration_count = 10000000;  // 10M iterations.
+  TestRunner test_runner;
+  std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct));
+
+  // Baseline tests to figure out the overhead of buffer resizing and data
+  // transfers.
+  for (size_t len : {0, 1, 9, 66, 259}) {
+    auto serialize_base_test =
+        std::bind(&SerializeBaseTest, _1, _2, _3, _4, len);
+    auto deserialize_base_test =
+        std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len);
+    test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test),
+                            std::move(deserialize_base_test), len);
+  }
+
+  // Individual serialization/deserialization tests.
+  test_runner.AddTest("bool", true);
+  test_runner.AddTest("int32_t", 12);
+
+  for (size_t len : {0, 1, 8, 64, 256}) {
+    test_runner.AddTest(GenerateContainerName("string", len),
+                        std::string(len, '*'));
+  }
+  // Serialization is too slow to handle such large strings, add this test for
+  // deserialization only.
+  test_runner.AddDeserializationTest(GenerateContainerName("string", 10240),
+                                     std::string(10240, '*'));
+
+  for (size_t len : {0, 1, 8, 64, 256}) {
+    std::vector<int32_t> int_vector(len);
+    std::iota(int_vector.begin(), int_vector.end(), 0);
+    test_runner.AddTest(GenerateContainerName("vector<int32_t>", len),
+                        std::move(int_vector));
+  }
+
+  std::vector<std::string> vector_of_strings = {
+      "012345678901234567890123456789", "012345678901234567890123456789",
+      "012345678901234567890123456789", "012345678901234567890123456789",
+      "012345678901234567890123456789",
+  };
+  test_runner.AddTest(
+      GenerateContainerName("vector<string>", vector_of_strings.size()),
+      std::move(vector_of_strings));
+
+  test_runner.AddTest("tuple<int, bool, string, double>",
+                      std::make_tuple(123, true, std::string{"foobar"}, 1.1));
+
+  for (size_t len : {0, 1, 8, 64}) {
+    std::map<int, std::string> test_map;
+    for (size_t i = 0; i < len; i++)
+      test_map.emplace(i, std::to_string(i));
+    test_runner.AddTest(GenerateContainerName("map<int, string>", len),
+                        std::move(test_map));
+  }
+
+  for (size_t len : {0, 1, 8, 64}) {
+    std::unordered_map<int, std::string> test_map;
+    for (size_t i = 0; i < len; i++)
+      test_map.emplace(i, std::to_string(i));
+    test_runner.AddTest(
+        GenerateContainerName("unordered_map<int, string>", len),
+        std::move(test_map));
+  }
+
+  // BufferWrapper can't be used with deserialization tests right now because
+  // it requires external buffer to be filled in, which is not available.
+  std::vector<std::vector<uint8_t>> data_buffers;
+  for (size_t len : {0, 1, 8, 64, 256}) {
+    data_buffers.emplace_back(len);
+    test_runner.AddSerializationTest(
+        GenerateContainerName("BufferWrapper<uint8_t*>", len),
+        BufferWrapper<uint8_t*>(data_buffers.back().data(),
+                                data_buffers.back().size()));
+  }
+
+  // Various backing buffers to run the tests on.
+  std::vector<TestRunner::BufferInfo> buffers;
+
+  Payload buffer;
+  buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer,
+                       [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); },
+                       [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); },
+                       &buffer);
+
+  TestPayload tls_buffer;
+  buffers.emplace_back(
+      "TLS Buffer", &tls_buffer, &tls_buffer,
+      [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); },
+      [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer);
+
+  StaticBuffer static_buffer;
+  buffers.emplace_back(
+      "Static Buffer", &static_buffer, &static_buffer,
+      [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); },
+      [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); },
+      &static_buffer);
+
+  // Finally, run all the tests.
+  test_runner.RunTests(iteration_count, buffers);
+  return 0;
+}
diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp
new file mode 100644
index 0000000..76fd154
--- /dev/null
+++ b/libs/vr/libpdx/mock_tests.cpp
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+#include <pdx/mock_client_channel.h>
+#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) {
+  // Make sure all our interfaces are mocked out properly and mock instances
+  // can be created.
+  android::pdx::MockClientChannel client_channel;
+  android::pdx::MockClientChannelFactory client_channel_factory;
+  android::pdx::MockInputResourceMapper input_resource_mapper;
+  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/channel_handle.h b/libs/vr/libpdx/private/pdx/channel_handle.h
new file mode 100644
index 0000000..1e62d25
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/channel_handle.h
@@ -0,0 +1,123 @@
+#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_
+#define ANDROID_PDX_CHANNEL_HANDLE_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+
+enum class ChannelHandleMode {
+  Local,
+  Borrowed,
+  Remote,
+};
+
+class ChannelManagerInterface {
+ public:
+  virtual void CloseHandle(std::int32_t handle) = 0;
+
+ protected:
+  // Nobody should be allowed to delete the instance of channel manager
+  // through this interface.
+  virtual ~ChannelManagerInterface() = default;
+};
+
+class ChannelHandleBase {
+ public:
+  ChannelHandleBase() = default;
+  ChannelHandleBase(const int32_t& value) : value_{value} {}
+
+  ChannelHandleBase(const ChannelHandleBase&) = delete;
+  ChannelHandleBase& operator=(const ChannelHandleBase&) = delete;
+
+  std::int32_t value() const { return value_; }
+  bool valid() const { return value_ >= 0; }
+  explicit operator bool() const { return valid(); }
+
+  void Close() { value_ = kEmptyHandle; }
+
+ protected:
+  // Must not be used by itself. Must be derived from.
+  ~ChannelHandleBase() = default;
+  enum : std::int32_t { kEmptyHandle = -1 };
+
+  std::int32_t value_{kEmptyHandle};
+};
+
+template <ChannelHandleMode Mode>
+class ChannelHandle : public ChannelHandleBase {
+ public:
+  ChannelHandle() = default;
+  using ChannelHandleBase::ChannelHandleBase;
+  ChannelHandle(ChannelHandle&& other) : ChannelHandleBase{other.value_} {
+    other.value_ = kEmptyHandle;
+  }
+  ~ChannelHandle() = default;
+
+  ChannelHandle Duplicate() const { return ChannelHandle{value_}; }
+
+  ChannelHandle& operator=(ChannelHandle&& other) {
+    value_ = other.value_;
+    other.value_ = kEmptyHandle;
+    return *this;
+  }
+};
+
+template <>
+class ChannelHandle<ChannelHandleMode::Local> : public ChannelHandleBase {
+ public:
+  ChannelHandle() = default;
+  ChannelHandle(ChannelManagerInterface* manager, int32_t value)
+      : ChannelHandleBase{value}, manager_{manager} {}
+
+  ChannelHandle(const ChannelHandle&) = delete;
+  ChannelHandle& operator=(const ChannelHandle&) = delete;
+
+  ChannelHandle(ChannelHandle&& other)
+      : ChannelHandleBase{other.value_}, manager_{other.manager_} {
+    other.manager_ = nullptr;
+    other.value_ = kEmptyHandle;
+  }
+
+  ChannelHandle& operator=(ChannelHandle&& other) {
+    value_ = other.value_;
+    manager_ = other.manager_;
+    other.value_ = kEmptyHandle;
+    other.manager_ = nullptr;
+    return *this;
+  }
+
+  ~ChannelHandle() {
+    if (manager_)
+      manager_->CloseHandle(value_);
+  }
+
+  ChannelHandle<ChannelHandleMode::Borrowed> Borrow() const {
+    return ChannelHandle<ChannelHandleMode::Borrowed>{value_};
+  }
+
+  void Close() {
+    if (manager_)
+      manager_->CloseHandle(value_);
+    manager_ = nullptr;
+    value_ = kEmptyHandle;
+  }
+
+ private:
+  ChannelManagerInterface* manager_{nullptr};
+};
+
+using LocalChannelHandle = ChannelHandle<ChannelHandleMode::Local>;
+using BorrowedChannelHandle = ChannelHandle<ChannelHandleMode::Borrowed>;
+using RemoteChannelHandle = ChannelHandle<ChannelHandleMode::Remote>;
+
+// ChannelReference is a 32 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local channel
+// handle by calling Transaction.GetChannelHandle().
+using ChannelReference = int32_t;
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_CHANNEL_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h
new file mode 100644
index 0000000..656de7e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -0,0 +1,301 @@
+#ifndef ANDROID_PDX_CLIENT_H_
+#define ANDROID_PDX_CLIENT_H_
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <pdx/channel_handle.h>
+#include <pdx/client_channel.h>
+#include <pdx/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class Transaction;
+
+/*
+ * Base class of client-side service API classes.
+ */
+class Client {
+ public:
+  static const int64_t kInfiniteTimeout = -1;
+
+  virtual ~Client() = default;
+
+  /*
+   * Returns true if the Client instance successfully initialized, false
+   * otherwise. Subclasses that can fail to initialize must override this and
+   * AND their initialization result with this base class method's result.
+   *
+   * This method is not intended to perform initialization, only to report
+   * the status of the initialization.
+   */
+  virtual bool IsInitialized() const;
+
+  /*
+   * Returns the error code describing the Client initialization failure, or 0
+   * if there was no failure.
+   */
+  int error() const;
+
+  // Returns a reference to IPC channel handle.
+  LocalChannelHandle& GetChannelHandle();
+
+ protected:
+  friend Transaction;
+  explicit Client(std::unique_ptr<ClientChannel> channel);
+  explicit Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+                  int64_t timeout_ms = kInfiniteTimeout);
+
+  /*
+   * Called by Client::Connect() after successfully connecting to the service
+   * endpoint. Subclasses may override this method to perform additional setup,
+   * including sending messages to complete the connection process.
+   *
+   * Subclasses may call Client::Close() within this method to terminate the
+   * connection; Client::Connect() returns the negated error passed to
+   * Client::Close() when this happens.
+   */
+  virtual void OnConnect();
+
+  enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 };
+
+  Status<void> SendImpulse(int opcode);
+  Status<void> SendImpulse(int opcode, const void* buffer, size_t length);
+
+  /*
+   * Remote method call API using pdx::rpc serialization.
+   * Include pdx/rpc/remote_method.h to use these methods.
+   */
+  template <typename RemoteMethodType, typename... Args>
+  Status<typename RemoteMethodType::Return> InvokeRemoteMethod(Args&&... args);
+
+  template <typename RemoteMethodType, typename ReturnType, typename... Args>
+  Status<void> InvokeRemoteMethodInPlace(ReturnType* return_value,
+                                         Args&&... args);
+
+  /*
+   * Close the endpoint file descriptor and optionally indicate an error, which
+   * may be retrieved through error(). Subclasses may use this in their
+   * constructor to signal failure during initialization or at other times
+   * during operation.
+   */
+  void Close(int error);
+
+  /*
+   * Returns true if the client is connected to the service, false otherwise.
+   */
+  bool IsConnected() const;
+
+  /*
+   * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1)
+   * for no timeout. Auto-reconnect can only be enabled if the Client class
+   * was constructed with a ClientChannelFactory.
+   */
+  void EnableAutoReconnect(int64_t reconnect_timeout_ms);
+
+  /*
+   * Disables auto-reconnect.
+   */
+  void DisableAutoReconnect();
+
+  /*
+   * Returns an fd that the client may use to check/wait for asynchronous
+   * notifications to the channel. It is implementation dependent how the
+   * transport backend handles this feature, however all implementations must
+   * support at least POLLIN/EPOLLIN/readable.
+   *
+   * For uses that require more than one type of event, use
+   * ClientChannel::GetEventMask() to distinguish between events.
+   */
+  int event_fd() const;
+
+  /*
+   * Returns the underlying ClientChannel object.
+   */
+  ClientChannel* GetChannel() const { return channel_.get(); }
+  std::unique_ptr<ClientChannel>&& TakeChannel() { return std::move(channel_); }
+
+ private:
+  Client(const Client&) = delete;
+  void operator=(const Client&) = delete;
+
+  Status<void> CheckReconnect();
+  bool NeedToDisconnectChannel(int error) const;
+  void CheckDisconnect(int error);
+
+  template <typename T>
+  inline void CheckDisconnect(const Status<T>& status) {
+    if (!status)
+      CheckDisconnect(status.error());
+  }
+
+  std::unique_ptr<ClientChannel> channel_;
+  int error_{0};
+
+  // Reconnection state.
+  std::unique_ptr<ClientChannelFactory> channel_factory_;
+  int64_t reconnect_timeout_ms_{0};
+  bool auto_reconnect_enabled_{false};
+};
+
+/*
+ * Utility template base class for client-side service API classes. Handles
+ * initialization checks during allocation and automatically cleans up on
+ * failure.
+ *
+ * @tparam T Type of the class extending this one.
+ * @tparam C Client class to wrap. Defaults to the Client class.
+ */
+template <typename T, typename ParentClient = Client>
+class ClientBase : public ParentClient {
+ public:
+  // Type of the client this class wraps.
+  using ClientType = ParentClient;
+
+  static_assert(std::is_base_of<Client, ParentClient>::value,
+                "The provided parent client is not a Client subclass.");
+
+  /*
+   * Allocates a new instance of the superclass and checks for successful
+   * initialization.
+   *
+   * The variadic arguments must expand to match one of type T's constructors
+   * and are passed through unchanged. If a timeout is desired, subclasses are
+   * responsible for passing this through to the appropriate ClientBase
+   * constructor.
+   *
+   * Returns a unique_ptr to the new instance on success, or an empty unique_ptr
+   * otherwise.
+   */
+  template <typename... Args>
+  static inline std::unique_ptr<T> Create(Args&&... args) {
+    std::unique_ptr<T> client(new T(std::forward<Args>(args)...));
+    if (client->IsInitialized())
+      return client;
+    else
+      return nullptr;
+  }
+
+ protected:
+  /*
+   * Type of the base class. Useful for referencing the base class type and
+   * constructor in subclasses. Subclasses with non-public constructors
+   * must declare BASE a friend.
+   */
+  using BASE = ClientBase<T, ParentClient>;
+
+  /*
+   * Type of the unique_ptr deleter. Useful for friend declarations.
+   */
+  using deleter_type = typename std::unique_ptr<T>::deleter_type;
+
+  using ParentClient::ParentClient;
+};
+
+class Transaction final : public OutputResourceMapper,
+                          public InputResourceMapper {
+ public:
+  Transaction(Client& client);
+  ~Transaction();
+
+  template <typename T>
+  Status<T> Send(int opcode) {
+    return SendVector<T>(opcode, nullptr, 0, nullptr, 0);
+  }
+
+  template <typename T>
+  Status<T> Send(int opcode, const void* send_buffer, size_t send_length,
+                 void* receive_buffer, size_t receive_length) {
+    const bool send = (send_buffer && send_length);
+    const bool receive = (receive_buffer && receive_length);
+    const iovec send_vector = {const_cast<void*>(send_buffer), send_length};
+    const iovec receive_vector = {receive_buffer, receive_length};
+    return SendVector<T>(opcode, send ? &send_vector : nullptr, send ? 1 : 0,
+                         receive ? &receive_vector : nullptr, receive ? 1 : 0);
+  }
+
+  template <typename T>
+  Status<T> SendVector(int opcode, const iovec* send_vector, size_t send_count,
+                       const iovec* receive_vector, size_t receive_count) {
+    Status<T> ret;
+    SendTransaction(opcode, &ret, send_vector, send_count, receive_vector,
+                    receive_count);
+    return ret;
+  }
+
+  template <typename T, size_t send_count, size_t receive_count>
+  Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+                       const iovec (&receive_vector)[receive_count]) {
+    return SendVector<T>(opcode, send_vector, send_count, receive_vector,
+                         receive_count);
+  }
+
+  template <typename T, size_t send_count>
+  Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+                       std::nullptr_t) {
+    return SendVector<T>(opcode, send_vector, send_count, nullptr, 0);
+  }
+
+  template <typename T, size_t receive_count>
+  Status<T> SendVector(int opcode, std::nullptr_t,
+                       const iovec (&receive_vector)[receive_count]) {
+    return SendVector<T>(opcode, nullptr, 0, receive_vector, receive_count);
+  }
+
+  // OutputResourceMapper
+  Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+  Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+  Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) override;
+
+  // InputResourceMapper
+  bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+  bool GetChannelHandle(ChannelReference ref,
+                        LocalChannelHandle* handle) override;
+
+ private:
+  bool EnsureStateAllocated();
+  void SendTransaction(int opcode, Status<void>* ret, const iovec* send_vector,
+                       size_t send_count, const iovec* receive_vector,
+                       size_t receive_count);
+  void SendTransaction(int opcode, Status<int>* ret, const iovec* send_vector,
+                       size_t send_count, const iovec* receive_vector,
+                       size_t receive_count);
+  void SendTransaction(int opcode, Status<LocalHandle>* ret,
+                       const iovec* send_vector, size_t send_count,
+                       const iovec* receive_vector, size_t receive_count);
+  void SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+                       const iovec* send_vector, size_t send_count,
+                       const iovec* receive_vector, size_t receive_count);
+  void CheckDisconnect(int error);
+
+  template <typename T>
+  inline void CheckDisconnect(const Status<T>& status) {
+    if (!status)
+      CheckDisconnect(status.error());
+  }
+
+  Client& client_;
+  void* state_{nullptr};
+  bool state_allocated_{false};
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_CLIENT_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
new file mode 100644
index 0000000..dbfd626
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -0,0 +1,58 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class ClientChannel {
+ public:
+  virtual ~ClientChannel() = default;
+
+  // Returns a tag that uniquely identifies a specific underlying IPC transport.
+  virtual uint32_t GetIpcTag() const = 0;
+
+  virtual int event_fd() const = 0;
+  virtual Status<int> GetEventMask(int events) = 0;
+
+  virtual LocalChannelHandle& GetChannelHandle() = 0;
+  virtual void* AllocateTransactionState() = 0;
+  virtual void FreeTransactionState(void* state) = 0;
+
+  virtual Status<void> SendImpulse(int opcode, const void* buffer,
+                                   size_t length) = 0;
+
+  virtual Status<int> SendWithInt(void* transaction_state, int opcode,
+                                  const iovec* send_vector, size_t send_count,
+                                  const iovec* receive_vector,
+                                  size_t receive_count) = 0;
+  virtual Status<LocalHandle> SendWithFileHandle(
+      void* transaction_state, int opcode, const iovec* send_vector,
+      size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+  virtual Status<LocalChannelHandle> SendWithChannelHandle(
+      void* transaction_state, int opcode, const iovec* send_vector,
+      size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+
+  virtual FileReference PushFileHandle(void* transaction_state,
+                                       const LocalHandle& handle) = 0;
+  virtual FileReference PushFileHandle(void* transaction_state,
+                                       const BorrowedHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      void* transaction_state, const LocalChannelHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      void* transaction_state, const BorrowedChannelHandle& handle) = 0;
+  virtual bool GetFileHandle(void* transaction_state, FileReference ref,
+                             LocalHandle* handle) const = 0;
+  virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+                                LocalChannelHandle* handle) const = 0;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel_factory.h b/libs/vr/libpdx/private/pdx/client_channel_factory.h
new file mode 100644
index 0000000..a82ab70
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel_factory.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+
+#include <pdx/client_channel.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class ClientChannelFactory {
+ public:
+  virtual ~ClientChannelFactory() = default;
+
+  virtual Status<std::unique_ptr<ClientChannel>> Connect(
+      int64_t timeout_ms) const = 0;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/file_handle.h b/libs/vr/libpdx/private/pdx/file_handle.h
new file mode 100644
index 0000000..b3c3ad7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/file_handle.h
@@ -0,0 +1,141 @@
+#ifndef ANDROID_PDX_FILE_HANDLE_H_
+#define ANDROID_PDX_FILE_HANDLE_H_
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace android {
+namespace pdx {
+
+enum class FileHandleMode {
+  Local,
+  Remote,
+  Borrowed,
+};
+
+// Manages ownership, sharing, and lifetime of file descriptors.
+template <FileHandleMode Mode>
+class FileHandle {
+ public:
+  static constexpr int kEmptyFileHandle = -1;
+
+  // Constructs an empty FileHandle object.
+  FileHandle() : fd_(kEmptyFileHandle) {}
+
+  // Constructs a FileHandle from an integer file descriptor and takes
+  // ownership.
+  explicit FileHandle(int fd) : fd_(fd) {}
+
+  // Constructs a FileHandle by opening |path|. The arguments follow the
+  // semantics of open().
+  FileHandle(const std::string& path, int flags, mode_t mode = 0) {
+    fd_ = open(path.c_str(), flags, mode);
+  }
+
+  // Constructs a FileHandle by opening |path| relative to |dir_fd|, following
+  // the semantics of openat().
+  FileHandle(const int directory_fd, const std::string& path, int flags,
+             mode_t mode = 0) {
+    fd_ = openat(directory_fd, path.c_str(), flags, mode);
+  }
+
+  // Move constructor that assumes ownership of the file descriptor, leaving the
+  // other FileHandle object empty.
+  FileHandle(FileHandle&& other) {
+    fd_ = other.fd_;
+    other.fd_ = kEmptyFileHandle;
+  }
+
+  // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and
+  // handles in remote handle space are not duplicated.
+  static FileHandle AsDuplicate(const int fd) {
+    if (Mode == FileHandleMode::Local)
+      return FileHandle(dup(fd));
+    else
+      return FileHandle(fd);
+  }
+
+  // Destructor closes the file descriptor when non-empty.
+  ~FileHandle() { Close(); }
+
+  // Move assignment operator that assumes ownership of the underlying file
+  // descriptor, leaving the other FileHandle object empty.
+  FileHandle& operator=(FileHandle&& other) {
+    if (this != &other) {
+      Reset(other.fd_);
+      other.fd_ = kEmptyFileHandle;
+    }
+    return *this;
+  }
+
+  // Resets the underlying file handle to |fd|.
+  void Reset(int fd) {
+    Close();
+    fd_ = fd;
+  }
+
+  // Closes the underlying file descriptor when non-empty.
+  void Close() {
+    if (IsValid() && Mode == FileHandleMode::Local)
+      close(fd_);
+    fd_ = kEmptyFileHandle;
+  }
+
+  // Return the internal fd, passing ownership to the caller.
+  int Release() {
+    int release_fd = fd_;
+    fd_ = kEmptyFileHandle;
+    return release_fd;
+  }
+
+  // Duplicates the underlying file descriptor and returns a FileHandle that
+  // owns the new file descriptor. File descriptors are not duplicated when Mode
+  // is Remote or Borrowed.
+  FileHandle Duplicate() const {
+    return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_);
+  }
+
+  FileHandle<FileHandleMode::Borrowed> Borrow() const {
+    return FileHandle<FileHandleMode::Borrowed>(Get());
+  }
+
+  // Gets the underlying file descriptor value.
+  int Get() const { return fd_; }
+  bool IsValid() const { return fd_ >= 0; }
+  explicit operator bool() const { return IsValid(); }
+
+ private:
+  int fd_;
+
+  FileHandle(const FileHandle&) = delete;
+  void operator=(const FileHandle&) = delete;
+};
+
+// Alias for a file handle in the local process' handle space.
+using LocalHandle = FileHandle<FileHandleMode::Local>;
+
+// Alias for a file handle in another process' handle space. Handles returned
+// from pushing a file object or channel must be stored in this type of handle
+// class, which doesn't close the underlying file descriptor. The underlying
+// file descriptor in this wrapper should not be passed to close() because doing
+// so would close an unrelated file descriptor in the local handle space.
+using RemoteHandle = FileHandle<FileHandleMode::Remote>;
+
+// Alias for borrowed handles in the local process' handle space. A borrowed
+// file handle is not close() because this wrapper does not own the underlying
+// file descriptor. Care must be take to ensure that a borrowed file handle
+// remains valid during use.
+using BorrowedHandle = FileHandle<FileHandleMode::Borrowed>;
+
+// FileReference is a 16 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local file
+// handle by calling Transaction.GetFileHandle() on client side and
+// Message.GetFileHandle() on service side.
+using FileReference = int16_t;
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_FILE_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/message_reader.h b/libs/vr/libpdx/private/pdx/message_reader.h
new file mode 100644
index 0000000..bc280cf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_reader.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_READER_H_
+#define ANDROID_PDX_MESSAGE_READER_H_
+
+#include <memory>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class InputResourceMapper {
+ public:
+  virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0;
+  virtual bool GetChannelHandle(ChannelReference ref,
+                                LocalChannelHandle* handle) = 0;
+
+ protected:
+  virtual ~InputResourceMapper() = default;
+};
+
+class MessageReader {
+ public:
+  // Pointers to start/end of the region in the read buffer.
+  using BufferSection = std::pair<const void*, const void*>;
+
+  virtual BufferSection GetNextReadBufferSection() = 0;
+  virtual void ConsumeReadBufferSectionData(const void* new_start) = 0;
+  virtual InputResourceMapper* GetInputResourceMapper() = 0;
+
+ protected:
+  virtual ~MessageReader() = default;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/message_writer.h b/libs/vr/libpdx/private/pdx/message_writer.h
new file mode 100644
index 0000000..4a101d6
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_writer.h
@@ -0,0 +1,40 @@
+#ifndef ANDROID_PDX_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MESSAGE_WRITER_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class OutputResourceMapper {
+ public:
+  virtual Status<FileReference> PushFileHandle(const LocalHandle& handle) = 0;
+  virtual Status<FileReference> PushFileHandle(
+      const BorrowedHandle& handle) = 0;
+  virtual Status<FileReference> PushFileHandle(const RemoteHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) = 0;
+
+ protected:
+  virtual ~OutputResourceMapper() = default;
+};
+
+class MessageWriter {
+ public:
+  virtual void* GetNextWriteBufferSection(size_t size) = 0;
+  virtual OutputResourceMapper* GetOutputResourceMapper() = 0;
+
+ protected:
+  virtual ~MessageWriter() = default;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
new file mode 100644
index 0000000..561c939
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannel : public ClientChannel {
+ public:
+  MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+  MOCK_CONST_METHOD0(event_fd, int());
+  MOCK_METHOD1(GetEventMask, Status<int>(int));
+  MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
+  MOCK_METHOD0(AllocateTransactionState, void*());
+  MOCK_METHOD1(FreeTransactionState, void(void* state));
+  MOCK_METHOD3(SendImpulse,
+               Status<void>(int opcode, const void* buffer, size_t length));
+  MOCK_METHOD6(SendWithInt,
+               Status<int>(void* transaction_state, int opcode,
+                           const iovec* send_vector, size_t send_count,
+                           const iovec* receive_vector, size_t receive_count));
+  MOCK_METHOD6(SendWithFileHandle,
+               Status<LocalHandle>(void* transaction_state, int opcode,
+                                   const iovec* send_vector, size_t send_count,
+                                   const iovec* receive_vector,
+                                   size_t receive_count));
+  MOCK_METHOD6(SendWithChannelHandle,
+               Status<LocalChannelHandle>(void* transaction_state, int opcode,
+                                          const iovec* send_vector,
+                                          size_t send_count,
+                                          const iovec* receive_vector,
+                                          size_t receive_count));
+  MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+                                             const LocalHandle& handle));
+  MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+                                             const BorrowedHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               ChannelReference(void* transaction_state,
+                                const LocalChannelHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               ChannelReference(void* transaction_state,
+                                const BorrowedChannelHandle& handle));
+  MOCK_CONST_METHOD3(GetFileHandle,
+                     bool(void* transaction_state, FileReference ref,
+                          LocalHandle* handle));
+  MOCK_CONST_METHOD3(GetChannelHandle,
+                     bool(void* transaction_state, ChannelReference ref,
+                          LocalChannelHandle* handle));
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
new file mode 100644
index 0000000..0190f5e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannelFactory : public ClientChannelFactory {
+ public:
+  MOCK_CONST_METHOD1(
+      Connect, Status<std::unique_ptr<ClientChannel>>(int64_t timeout_ms));
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_reader.h b/libs/vr/libpdx/private/pdx/mock_message_reader.h
new file mode 100644
index 0000000..85e96ef
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_reader.h
@@ -0,0 +1,27 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_READER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_reader.h>
+
+namespace android {
+namespace pdx {
+
+class MockInputResourceMapper : public InputResourceMapper {
+ public:
+  MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle));
+  MOCK_METHOD2(GetChannelHandle,
+               bool(ChannelReference ref, LocalChannelHandle* handle));
+};
+
+class MockMessageReader : public MessageReader {
+ public:
+  MOCK_METHOD0(GetNextReadBufferSection, BufferSection());
+  MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start));
+  MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*());
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MOCK_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_writer.h b/libs/vr/libpdx/private/pdx/mock_message_writer.h
new file mode 100644
index 0000000..e06e5bb
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_writer.h>
+
+namespace android {
+namespace pdx {
+
+class MockOutputResourceMapper : public OutputResourceMapper {
+ public:
+  MOCK_METHOD1(PushFileHandle,
+               Status<FileReference>(const LocalHandle& handle));
+  MOCK_METHOD1(PushFileHandle,
+               Status<FileReference>(const BorrowedHandle& handle));
+  MOCK_METHOD1(PushFileHandle,
+               Status<FileReference>(const RemoteHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               Status<ChannelReference>(const LocalChannelHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               Status<ChannelReference>(const BorrowedChannelHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               Status<ChannelReference>(const RemoteChannelHandle& handle));
+};
+
+class MockMessageWriter : public MessageWriter {
+ public:
+  MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size));
+  MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*());
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
new file mode 100644
index 0000000..9b51d30
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
@@ -0,0 +1,24 @@
+#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
new file mode 100644
index 0000000..e741d4a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -0,0 +1,74 @@
+#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_
+#define ANDROID_PDX_MOCK_ENDPOINT_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+
+class MockEndpoint : public Endpoint {
+ public:
+  MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+  MOCK_METHOD1(SetService, Status<void>(Service* service));
+  MOCK_METHOD2(SetChannel, Status<void>(int channel_id, Channel* channel));
+  MOCK_METHOD1(CloseChannel, Status<void>(int channel_id));
+  MOCK_METHOD3(ModifyChannelEvents,
+               Status<void>(int channel_id, int clear_mask, int set_mask));
+  MOCK_METHOD4(PushChannel,
+               Status<RemoteChannelHandle>(Message* message, int flags,
+                                           Channel* channel, int* channel_id));
+  MOCK_METHOD3(CheckChannel,
+               Status<int>(const Message* message, ChannelReference ref,
+                           Channel** channel));
+  MOCK_METHOD1(MessageReceive, Status<void>(Message* message));
+  MOCK_METHOD2(MessageReply, Status<void>(Message* message, int return_code));
+  MOCK_METHOD2(MessageReplyFd,
+               Status<void>(Message* message, unsigned int push_fd));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               Status<void>(Message* message,
+                            const LocalChannelHandle& handle));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               Status<void>(Message* message,
+                            const BorrowedChannelHandle& handle));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               Status<void>(Message* message,
+                            const RemoteChannelHandle& handle));
+  MOCK_METHOD3(ReadMessageData,
+               Status<size_t>(Message* message, const iovec* vector,
+                              size_t vector_length));
+  MOCK_METHOD3(WriteMessageData,
+               Status<size_t>(Message* message, const iovec* vector,
+                              size_t vector_length));
+  MOCK_METHOD2(PushFileHandle,
+               Status<FileReference>(Message* message,
+                                     const LocalHandle& handle));
+  MOCK_METHOD2(PushFileHandle,
+               Status<FileReference>(Message* message,
+                                     const BorrowedHandle& handle));
+  MOCK_METHOD2(PushFileHandle,
+               Status<FileReference>(Message* message,
+                                     const RemoteHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               Status<ChannelReference>(Message* message,
+                                        const LocalChannelHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               Status<ChannelReference>(Message* message,
+                                        const BorrowedChannelHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               Status<ChannelReference>(Message* message,
+                                        const RemoteChannelHandle& handle));
+  MOCK_CONST_METHOD2(GetFileHandle,
+                     LocalHandle(Message* message, FileReference ref));
+  MOCK_CONST_METHOD2(GetChannelHandle,
+                     LocalChannelHandle(Message* message,
+                                        ChannelReference ref));
+  MOCK_METHOD0(AllocateMessageState, void*());
+  MOCK_METHOD1(FreeMessageState, void(void* state));
+  MOCK_METHOD0(Cancel, Status<void>());
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_MOCK_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
new file mode 100644
index 0000000..e006284
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
@@ -0,0 +1,184 @@
+#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+#define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/serialization.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides automatic serialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+//     ArgumentEncoder<int(int, float)> encoder(writer);
+//     encoder.EncodeArguments(1, 1.0);
+
+template <typename T>
+class ArgumentEncoder;
+
+// Specialization of ArgumentEncoder for void return types.
+template <typename... Args>
+class ArgumentEncoder<void(Args...)> {
+ public:
+  explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+  // Serializes the arguments as a tuple.
+  void EncodeArguments(Args... args) {
+    Serialize(std::forward_as_tuple(args...), writer_);
+  }
+
+ private:
+  MessageWriter* writer_;
+};
+
+// Specialization of ArgumentEncoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentEncoder<Return(Args...)> {
+ public:
+  // Simplified types with reference and cv removed.
+  using ReturnType = typename std::decay<Return>::type;
+
+  explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+  // Serializes the arguments as a tuple.
+  void EncodeArguments(Args... args) {
+    Serialize(std::forward_as_tuple(args...), writer_);
+  }
+
+  // Serializes the return value for rvalue references.
+  void EncodeReturn(const ReturnType& return_value) {
+    Serialize(return_value, writer_);
+  }
+
+ private:
+  MessageWriter* writer_;
+};
+
+// Utility to build an ArgumentEncoder from a function pointer and a message
+// writer.
+template <typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+    Return (*)(Args...), MessageWriter* writer) {
+  return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a method pointer and a message
+// writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+    Return (Class::*)(Args...), MessageWriter* writer) {
+  return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a const method pointer and a
+// message writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+    Return (Class::*)(Args...) const, MessageWriter* writer) {
+  return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a function type and a message
+// writer.
+template <typename Signature>
+inline ArgumentEncoder<Signature> MakeArgumentEncoder(MessageWriter* writer) {
+  return ArgumentEncoder<Signature>(writer);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Provides automatic deserialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+//     auto decoder = MakeArgumentDecoder<std::string(void)>(reader);
+//     ErrorType error = decoder.DecodeReturn(&return_value);
+
+template <typename T>
+class ArgumentDecoder;
+
+// Specialization of ArgumentDecoder for void return types.
+template <typename... Args>
+class ArgumentDecoder<void(Args...)> {
+ public:
+  // Simplified types with reference and cv removed.
+  using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+
+  explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+  // Deserializes arguments into a tuple.
+  ArgsTupleType DecodeArguments(ErrorType* error) {
+    ArgsTupleType value;
+    *error = Deserialize(&value, reader_);
+    return value;
+  }
+
+ private:
+  MessageReader* reader_;
+};
+
+// Specialization of ArgumentDecoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentDecoder<Return(Args...)> {
+ public:
+  // Simplified types with reference and cv removed.
+  using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+  using ReturnType = typename std::decay<Return>::type;
+
+  explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+  // Deserializes arguments into a tuple.
+  ArgsTupleType DecodeArguments(ErrorType* error) {
+    ArgsTupleType value;
+    *error = Deserialize(&value, reader_);
+    return value;
+  }
+
+  // Deserializes the return value.
+  ErrorType DecodeReturn(ReturnType* value) {
+    return Deserialize(value, reader_);
+  }
+
+ private:
+  MessageReader* reader_;
+};
+
+// Utility to build an ArgumentDecoder from a function pointer and a message
+// reader.
+template <typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+    Return (*)(Args...), MessageReader* reader) {
+  return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a method pointer and a message
+// reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+    Return (Class::*)(Args...), MessageReader* reader) {
+  return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a const method pointer and a
+// message reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+    Return (Class::*)(Args...) const, MessageReader* reader) {
+  return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a function type and a message
+// reader.
+template <typename Signature>
+inline ArgumentDecoder<Signature> MakeArgumentDecoder(MessageReader* reader) {
+  return ArgumentDecoder<Signature>(reader);
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
new file mode 100644
index 0000000..93d87f3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
@@ -0,0 +1,111 @@
+#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+#define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C array buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::vector, and may be substituted for std::vector during
+// serialization and deserialization. This substitution makes handling of C
+// arrays more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::vector arguments or return values.
+template <typename T>
+class ArrayWrapper {
+ public:
+  // Define types in the style of STL containers to support STL operators.
+  typedef T value_type;
+  typedef std::size_t size_type;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef T* pointer;
+  typedef const T* const_pointer;
+
+  ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+  ArrayWrapper(pointer buffer, size_type capacity, size_type size)
+      : buffer_(&buffer[0]),
+        capacity_(capacity),
+        end_(capacity < size ? capacity : size) {}
+
+  ArrayWrapper(pointer buffer, size_type size)
+      : ArrayWrapper(buffer, size, size) {}
+
+  ArrayWrapper(const ArrayWrapper& other) { *this = other; }
+
+  ArrayWrapper(ArrayWrapper&& other) { *this = std::move(other); }
+
+  ArrayWrapper& operator=(const ArrayWrapper& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+    }
+
+    return *this;
+  }
+
+  ArrayWrapper& operator=(ArrayWrapper&& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+      other.buffer_ = nullptr;
+      other.capacity_ = 0;
+      other.end_ = 0;
+    }
+
+    return *this;
+  }
+
+  pointer data() { return buffer_; }
+  const_pointer data() const { return buffer_; }
+
+  pointer begin() { return &buffer_[0]; }
+  pointer end() { return &buffer_[end_]; }
+  const_pointer begin() const { return &buffer_[0]; }
+  const_pointer end() const { return &buffer_[end_]; }
+
+  size_type size() const { return end_; }
+  size_type max_size() const { return capacity_; }
+  size_type capacity() const { return capacity_; }
+
+  // Moves the end marker to |size|, clamping the end marker to the max capacity
+  // of the underlying array. This method does not change the size of the
+  // underlying array.
+  void resize(size_type size) {
+    if (size <= capacity_)
+      end_ = size;
+    else
+      end_ = capacity_;
+  }
+
+  reference operator[](size_type pos) { return buffer_[pos]; }
+  const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+  pointer buffer_;
+  size_type capacity_;
+  size_type end_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+ArrayWrapper<T> WrapArray(T* buffer, SizeType size) {
+  return ArrayWrapper<T>(buffer, size);
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
new file mode 100644
index 0000000..aa86531
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
@@ -0,0 +1,177 @@
+#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+#define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class supports serialization of
+// buffers as raw bytes.
+template <typename T>
+class BufferWrapper;
+
+template <typename T>
+class BufferWrapper<T*> {
+ public:
+  // Define types in the style of STL containers to support STL operators.
+  typedef T value_type;
+  typedef std::size_t size_type;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef T* pointer;
+  typedef const T* const_pointer;
+
+  BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+  BufferWrapper(pointer buffer, size_type capacity, size_type size)
+      : buffer_(&buffer[0]),
+        capacity_(capacity),
+        end_(capacity < size ? capacity : size) {}
+
+  BufferWrapper(pointer buffer, size_type size)
+      : BufferWrapper(buffer, size, size) {}
+
+  BufferWrapper(const BufferWrapper& other) { *this = other; }
+
+  BufferWrapper(BufferWrapper&& other) { *this = std::move(other); }
+
+  BufferWrapper& operator=(const BufferWrapper& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+    }
+
+    return *this;
+  }
+
+  BufferWrapper& operator=(BufferWrapper&& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+      other.buffer_ = nullptr;
+      other.capacity_ = 0;
+      other.end_ = 0;
+    }
+
+    return *this;
+  }
+
+  pointer data() { return buffer_; }
+  const_pointer data() const { return buffer_; }
+
+  pointer begin() { return &buffer_[0]; }
+  pointer end() { return &buffer_[end_]; }
+  const_pointer begin() const { return &buffer_[0]; }
+  const_pointer end() const { return &buffer_[end_]; }
+
+  size_type size() const { return end_; }
+  size_type max_size() const { return capacity_; }
+  size_type capacity() const { return capacity_; }
+
+  void resize(size_type size) {
+    if (size <= capacity_)
+      end_ = size;
+    else
+      end_ = capacity_;
+  }
+
+  reference operator[](size_type pos) { return buffer_[pos]; }
+  const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+  pointer buffer_;
+  size_type capacity_;
+  size_type end_;
+};
+
+template <typename T, typename Allocator>
+class BufferWrapper<std::vector<T, Allocator>> {
+ public:
+  using BufferType = typename std::vector<T, Allocator>;
+  using value_type = typename BufferType::value_type;
+  using size_type = typename BufferType::size_type;
+  using reference = typename BufferType::reference;
+  using const_reference = typename BufferType::const_reference;
+  using pointer = typename BufferType::pointer;
+  using const_pointer = typename BufferType::const_pointer;
+  using iterator = typename BufferType::iterator;
+  using const_iterator = typename BufferType::const_iterator;
+
+  BufferWrapper() {}
+  BufferWrapper(const BufferType& buffer) : buffer_(buffer) {}
+  BufferWrapper(const BufferType& buffer, const Allocator& allocator)
+      : buffer_(buffer, allocator) {}
+  BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {}
+  BufferWrapper(BufferType&& buffer, const Allocator& allocator)
+      : buffer_(std::move(buffer), allocator) {}
+  BufferWrapper(const BufferWrapper&) = default;
+  BufferWrapper(BufferWrapper&&) = default;
+  BufferWrapper& operator=(const BufferWrapper&) = default;
+  BufferWrapper& operator=(BufferWrapper&&) = default;
+
+  pointer data() { return buffer_.data(); }
+  const_pointer data() const { return buffer_.data(); }
+
+  iterator begin() { return buffer_.begin(); }
+  iterator end() { return buffer_.end(); }
+  const_iterator begin() const { return buffer_.begin(); }
+  const_iterator end() const { return buffer_.end(); }
+
+  size_type size() const { return buffer_.size(); }
+  size_type max_size() const { return buffer_.capacity(); }
+  size_type capacity() const { return buffer_.capacity(); }
+
+  void resize(size_type size) { buffer_.resize(size); }
+  void reserve(size_type size) { buffer_.reserve(size); }
+
+  reference operator[](size_type pos) { return buffer_[pos]; }
+  const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+  BufferType& buffer() { return buffer_; }
+  const BufferType& buffer() const { return buffer_; }
+
+ private:
+  BufferType buffer_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+BufferWrapper<T*> WrapBuffer(T* buffer, SizeType size) {
+  return BufferWrapper<T*>(buffer, size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<std::uint8_t*> WrapBuffer(void* buffer, SizeType size) {
+  return BufferWrapper<std::uint8_t*>(static_cast<std::uint8_t*>(buffer), size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<const std::uint8_t*> WrapBuffer(const void* buffer,
+                                              SizeType size) {
+  return BufferWrapper<const std::uint8_t*>(
+      static_cast<const std::uint8_t*>(buffer), size);
+}
+
+template <typename T, typename Allocator = std::allocator<T>>
+BufferWrapper<std::vector<T, Allocator>> WrapBuffer(
+    std::vector<T, Allocator>&& buffer) {
+  return BufferWrapper<std::vector<T, Allocator>>(
+      std::forward<std::vector<T, Allocator>>(buffer));
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
new file mode 100644
index 0000000..5ce34f8
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+#define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Copies const, void, and reference qualifiers from type T to type U, such that
+// the new type U' carries the same cv-reference qualifiers as T, with the same
+// underlying type as U.
+template <typename T, typename U>
+class CopyCVReference {
+ private:
+  using R = typename std::remove_reference<T>::type;
+  using U1 =
+      typename std::conditional<std::is_const<R>::value,
+                                typename std::add_const<U>::type, U>::type;
+  using U2 =
+      typename std::conditional<std::is_volatile<R>::value,
+                                typename std::add_volatile<U1>::type, U1>::type;
+  using U3 =
+      typename std::conditional<std::is_lvalue_reference<T>::value,
+                                typename std::add_lvalue_reference<U2>::type,
+                                U2>::type;
+  using U4 =
+      typename std::conditional<std::is_rvalue_reference<T>::value,
+                                typename std::add_rvalue_reference<U3>::type,
+                                U3>::type;
+
+ public:
+  using Type = U4;
+};
+
+template <typename T, typename U>
+using CopyCVReferenceType = typename CopyCVReference<T, U>::Type;
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  //  ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
new file mode 100644
index 0000000..b6e2980
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+#define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Allocator adaptor that interposes construct() calls to convert value
+// initialization into default initialization. All standard containers
+// value-initialize their elements when constructed with a single size_type
+// argument or when grown by a call to resize. This allocator avoids potentially
+// costly value-initialization in these situations for value types that are
+// default constructible. As a consequence, elements of non-class types are left
+// uninitialized; this is desirable when using std::vector as a resizable
+// buffer, for example.
+template <typename T, typename Allocator = std::allocator<T>>
+class DefaultInitializationAllocator : public Allocator {
+  typedef std::allocator_traits<Allocator> AllocatorTraits;
+
+ public:
+  template <typename U>
+  struct rebind {
+    using other = DefaultInitializationAllocator<
+        U, typename AllocatorTraits::template rebind_alloc<U>>;
+  };
+
+  using Allocator::Allocator;
+
+  template <typename U>
+  void construct(U* pointer) noexcept(
+      std::is_nothrow_default_constructible<U>::value) {
+    ::new (static_cast<void*>(pointer)) U;
+  }
+  template <typename U, typename... Args>
+  void construct(U* pointer, Args&&... args) {
+    AllocatorTraits::construct(static_cast<Allocator&>(*this), pointer,
+                               std::forward<Args>(args)...);
+  }
+};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  //  ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/encoding.h b/libs/vr/libpdx/private/pdx/rpc/encoding.h
new file mode 100644
index 0000000..f51d807
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/encoding.h
@@ -0,0 +1,616 @@
+#ifndef ANDROID_PDX_RPC_ENCODING_H_
+#define ANDROID_PDX_RPC_ENCODING_H_
+
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <map>
+#include <numeric>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+#include "array_wrapper.h"
+#include "buffer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This library uses a subset, or profile, of MessagePack (http://msgpack.org)
+// to encode supported data types during serialization and to verify the
+// expected data types during deserialization. One notable deviation from the
+// MessagePack specification is that little-endian byte order is used for
+// multi-byte numeric types to avoid unnecessary conversion on nearly all
+// relevant architectures.
+//
+// Some data types, integers for example, support multiple encoding strategies.
+// This library attempts to optimize for space based on the value of such types.
+// However, during decode all valid encodings for a given type are accepted.
+
+// Prefix byte for type encodings. This is the complete list of prefix bytes
+// from the MessagePack specification, even though not all types are used by
+// this library.
+enum EncodingPrefix {
+  ENCODING_TYPE_POSITIVE_FIXINT = 0x00,
+  ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00,
+  ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f,
+  ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f,
+  ENCODING_TYPE_FIXMAP = 0x80,
+  ENCODING_TYPE_FIXMAP_MIN = 0x80,
+  ENCODING_TYPE_FIXMAP_MAX = 0x8f,
+  ENCODING_TYPE_FIXMAP_MASK = 0x0f,
+  ENCODING_TYPE_FIXARRAY = 0x90,
+  ENCODING_TYPE_FIXARRAY_MIN = 0x90,
+  ENCODING_TYPE_FIXARRAY_MAX = 0x9f,
+  ENCODING_TYPE_FIXARRAY_MASK = 0x0f,
+  ENCODING_TYPE_FIXSTR = 0xa0,
+  ENCODING_TYPE_FIXSTR_MIN = 0xa0,
+  ENCODING_TYPE_FIXSTR_MAX = 0xbf,
+  ENCODING_TYPE_FIXSTR_MASK = 0x1f,
+  ENCODING_TYPE_NIL = 0xc0,
+  ENCODING_TYPE_RESERVED = 0xc1,
+  ENCODING_TYPE_FALSE = 0xc2,
+  ENCODING_TYPE_TRUE = 0xc3,
+  ENCODING_TYPE_BIN8 = 0xc4,
+  ENCODING_TYPE_BIN16 = 0xc5,
+  ENCODING_TYPE_BIN32 = 0xc6,
+  ENCODING_TYPE_EXT8 = 0xc7,
+  ENCODING_TYPE_EXT16 = 0xc8,
+  ENCODING_TYPE_EXT32 = 0xc9,
+  ENCODING_TYPE_FLOAT32 = 0xca,
+  ENCODING_TYPE_FLOAT64 = 0xcb,
+  ENCODING_TYPE_UINT8 = 0xcc,
+  ENCODING_TYPE_UINT16 = 0xcd,
+  ENCODING_TYPE_UINT32 = 0xce,
+  ENCODING_TYPE_UINT64 = 0xcf,
+  ENCODING_TYPE_INT8 = 0xd0,
+  ENCODING_TYPE_INT16 = 0xd1,
+  ENCODING_TYPE_INT32 = 0xd2,
+  ENCODING_TYPE_INT64 = 0xd3,
+  ENCODING_TYPE_FIXEXT1 = 0xd4,
+  ENCODING_TYPE_FIXEXT2 = 0xd5,
+  ENCODING_TYPE_FIXEXT4 = 0xd6,
+  ENCODING_TYPE_FIXEXT8 = 0xd7,
+  ENCODING_TYPE_FIXEXT16 = 0xd8,
+  ENCODING_TYPE_STR8 = 0xd9,
+  ENCODING_TYPE_STR16 = 0xda,
+  ENCODING_TYPE_STR32 = 0xdb,
+  ENCODING_TYPE_ARRAY16 = 0xdc,
+  ENCODING_TYPE_ARRAY32 = 0xdd,
+  ENCODING_TYPE_MAP16 = 0xde,
+  ENCODING_TYPE_MAP32 = 0xdf,
+  ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0,
+  ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0,
+  ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff,
+};
+
+// Base encoding classes grouping multi-strategy encodings.
+enum EncodingClass {
+  ENCODING_CLASS_BOOL,
+  ENCODING_CLASS_NIL,
+  ENCODING_CLASS_INT,
+  ENCODING_CLASS_UINT,
+  ENCODING_CLASS_FLOAT,
+  ENCODING_CLASS_ARRAY,
+  ENCODING_CLASS_MAP,
+  ENCODING_CLASS_STRING,
+  ENCODING_CLASS_BINARY,
+  ENCODING_CLASS_EXTENSION,
+};
+
+// Encoding prefixes are unsigned bytes.
+typedef std::uint8_t EncodingType;
+
+// Extension encoding types defined by this library.
+enum EncodingExtType : int8_t {
+  ENCODING_EXT_TYPE_FILE_DESCRIPTOR,
+  ENCODING_EXT_TYPE_CHANNEL_HANDLE,
+};
+
+// Encoding predicates. Determines whether the given encoding is of a specific
+// type.
+inline constexpr bool IsFixintEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsInt8Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+    case ENCODING_TYPE_INT8:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsUInt8Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_UINT8:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsInt16Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+    case ENCODING_TYPE_INT8:
+    case ENCODING_TYPE_INT16:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsUInt16Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_UINT8:
+    case ENCODING_TYPE_UINT16:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsInt32Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+    case ENCODING_TYPE_INT8:
+    case ENCODING_TYPE_INT16:
+    case ENCODING_TYPE_INT32:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsUInt32Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_UINT8:
+    case ENCODING_TYPE_UINT16:
+    case ENCODING_TYPE_UINT32:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsInt64Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+    case ENCODING_TYPE_INT8:
+    case ENCODING_TYPE_INT16:
+    case ENCODING_TYPE_INT32:
+    case ENCODING_TYPE_INT64:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsUInt64Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_UINT8:
+    case ENCODING_TYPE_UINT16:
+    case ENCODING_TYPE_UINT32:
+    case ENCODING_TYPE_UINT64:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFixmapEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFixarrayEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFixstrEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFixextEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FIXEXT1:
+    case ENCODING_TYPE_FIXEXT2:
+    case ENCODING_TYPE_FIXEXT4:
+    case ENCODING_TYPE_FIXEXT8:
+    case ENCODING_TYPE_FIXEXT16:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFloat32Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FLOAT32:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsFloat64Encoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FLOAT32:
+    case ENCODING_TYPE_FLOAT64:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr bool IsBoolEncoding(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FALSE:
+    case ENCODING_TYPE_TRUE:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline constexpr std::size_t GetFixstrSize(EncodingType encoding) {
+  return encoding & ENCODING_TYPE_FIXSTR_MASK;
+}
+
+inline constexpr std::size_t GetFixarraySize(EncodingType encoding) {
+  return encoding & ENCODING_TYPE_FIXARRAY_MASK;
+}
+
+inline constexpr std::size_t GetFixmapSize(EncodingType encoding) {
+  return encoding & ENCODING_TYPE_FIXMAP_MASK;
+}
+
+inline constexpr std::size_t GetFixextSize(EncodingType encoding) {
+  switch (encoding) {
+    case ENCODING_TYPE_FIXEXT1:
+      return 1;
+    case ENCODING_TYPE_FIXEXT2:
+      return 2;
+    case ENCODING_TYPE_FIXEXT4:
+      return 4;
+    case ENCODING_TYPE_FIXEXT8:
+      return 8;
+    case ENCODING_TYPE_FIXEXT16:
+      return 16;
+    default:
+      return 0;  // Invalid fixext size.
+  }
+}
+
+// Gets the size of the encoding in bytes, not including external payload data.
+inline constexpr std::size_t GetEncodingSize(EncodingType encoding) {
+  switch (encoding) {
+    // Encoding is fully contained within the type value.
+    case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+    case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+    case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+    case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+    case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+    case ENCODING_TYPE_NIL:
+    case ENCODING_TYPE_RESERVED:
+    case ENCODING_TYPE_FALSE:
+    case ENCODING_TYPE_TRUE:
+      return 1;
+
+    // Encoding type followed by one-byte size or immediate value.
+    case ENCODING_TYPE_BIN8:
+    case ENCODING_TYPE_EXT8:
+    case ENCODING_TYPE_UINT8:
+    case ENCODING_TYPE_INT8:
+    case ENCODING_TYPE_STR8:
+    // Encoding type followed by one-byte extension type.
+    case ENCODING_TYPE_FIXEXT1:
+    case ENCODING_TYPE_FIXEXT2:
+    case ENCODING_TYPE_FIXEXT4:
+    case ENCODING_TYPE_FIXEXT8:
+    case ENCODING_TYPE_FIXEXT16:
+      return 2;
+
+    // Encoding type followed by two-byte size or immediate value.
+    case ENCODING_TYPE_BIN16:
+    case ENCODING_TYPE_EXT16:
+    case ENCODING_TYPE_UINT16:
+    case ENCODING_TYPE_INT16:
+    case ENCODING_TYPE_STR16:
+    case ENCODING_TYPE_ARRAY16:
+    case ENCODING_TYPE_MAP16:
+      return 3;
+
+    // Encoding type followed by four-byte size or immediate value.
+    case ENCODING_TYPE_BIN32:
+    case ENCODING_TYPE_EXT32:
+    case ENCODING_TYPE_FLOAT32:
+    case ENCODING_TYPE_UINT32:
+    case ENCODING_TYPE_INT32:
+    case ENCODING_TYPE_STR32:
+    case ENCODING_TYPE_ARRAY32:
+    case ENCODING_TYPE_MAP32:
+      return 5;
+
+    // Encoding type followed by eight-byte immediate value.
+    case ENCODING_TYPE_FLOAT64:
+    case ENCODING_TYPE_UINT64:
+    case ENCODING_TYPE_INT64:
+      return 9;
+
+    default:
+      return 0;
+  }
+}
+
+// Encoding for standard types. Each supported data type has an associated
+// encoding or set of encodings. These functions determine the MessagePack
+// encoding based on the data type, value, and size of their arguments.
+
+inline constexpr EncodingType EncodeArrayType(std::size_t size) {
+  if (size < (1U << 4))
+    return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK);
+  else if (size < (1U << 16))
+    return ENCODING_TYPE_ARRAY16;
+  else
+    return ENCODING_TYPE_ARRAY32;
+}
+
+inline constexpr EncodingType EncodeMapType(std::size_t size) {
+  if (size < (1U << 4))
+    return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK);
+  else if (size < (1U << 16))
+    return ENCODING_TYPE_MAP16;
+  else
+    return ENCODING_TYPE_MAP32;
+}
+
+inline constexpr EncodingType EncodeStringType(std::size_t size) {
+  if (size < (1U << 5))
+    return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK);
+  else if (size < (1U << 8))
+    return ENCODING_TYPE_STR8;
+  else if (size < (1U << 16))
+    return ENCODING_TYPE_STR16;
+  else
+    return ENCODING_TYPE_STR32;
+}
+
+inline constexpr EncodingType EncodeBinType(std::size_t size) {
+  if (size < (1U << 8))
+    return ENCODING_TYPE_BIN8;
+  else if (size < (1U << 16))
+    return ENCODING_TYPE_BIN16;
+  else
+    return ENCODING_TYPE_BIN32;
+}
+
+inline EncodingType EncodeType(const EmptyVariant& /*empty*/) {
+  return ENCODING_TYPE_NIL;
+}
+
+// Variant is encoded as a single-element map, with the type index as the key.
+template <typename... Types>
+inline EncodingType EncodeType(const Variant<Types...>& /*variant*/) {
+  return EncodeMapType(1);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const StringWrapper<T>& value) {
+  return EncodeStringType(value.length());
+}
+
+inline constexpr EncodingType EncodeType(const std::string& value) {
+  return EncodeStringType(value.length());
+}
+
+template <typename T, std::size_t Size>
+inline constexpr EncodingType EncodeType(const std::array<T, Size>& /*value*/) {
+  return EncodeArrayType(Size);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const ArrayWrapper<T>& value) {
+  return EncodeArrayType(value.size());
+}
+
+template <typename T, typename Allocator>
+inline constexpr EncodingType EncodeType(
+    const std::vector<T, Allocator>& value) {
+  return EncodeArrayType(value.size());
+}
+
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline constexpr EncodingType EncodeType(
+    const std::map<Key, T, Compare, Allocator>& value) {
+  return EncodeMapType(value.size());
+}
+
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline constexpr EncodingType EncodeType(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value) {
+  return EncodeMapType(value.size());
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const BufferWrapper<T>& value) {
+  // BIN size is in bytes.
+  return EncodeBinType(value.size() *
+                       sizeof(typename BufferWrapper<T>::value_type));
+}
+
+template <typename T, typename U>
+inline constexpr EncodingType EncodeType(const std::pair<T, U>& /*value*/) {
+  return EncodeArrayType(2);
+}
+
+template <typename... T>
+inline constexpr EncodingType EncodeType(const std::tuple<T...>& /*value*/) {
+  return EncodeArrayType(sizeof...(T));
+}
+
+// FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor"
+// and a signed 16-bit index into the pushed fd array. Empty file descriptor
+// have an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr EncodingType EncodeType(const FileHandle<Mode>& /*fd*/) {
+  return ENCODING_TYPE_FIXEXT2;
+}
+
+// ChannelHandle is encoded as a FIXEXT4 with a type of
+// ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing
+// a client channel in a remote process. Empty handle has a value of -1.
+template <ChannelHandleMode Mode>
+inline constexpr EncodingType EncodeType(
+    const ChannelHandle<Mode>& /*handle*/) {
+  return ENCODING_TYPE_FIXEXT4;
+}
+
+inline constexpr EncodingType EncodeType(const bool& value) {
+  return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE;
+}
+
+// Type 'char' is a little bit special in that it is distinct from 'signed char'
+// and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for
+// encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT.
+inline constexpr EncodingType EncodeType(const char& value) {
+  if (value < static_cast<char>(1 << 7))
+    return value;
+  else
+    return ENCODING_TYPE_UINT8;
+}
+
+inline constexpr EncodingType EncodeType(const uint8_t& value) {
+  if (value < (1U << 7))
+    return value;
+  else
+    return ENCODING_TYPE_UINT8;
+}
+inline constexpr EncodingType EncodeType(const int8_t& value) {
+  if (value >= -32)
+    return value;
+  else
+    return ENCODING_TYPE_INT8;
+}
+inline constexpr EncodingType EncodeType(const uint16_t& value) {
+  if (value < (1U << 7))
+    return static_cast<EncodingType>(value);
+  else if (value < (1U << 8))
+    return ENCODING_TYPE_UINT8;
+  else
+    return ENCODING_TYPE_UINT16;
+}
+inline constexpr EncodingType EncodeType(const int16_t& value) {
+  if (value >= -32 && value <= 127)
+    return static_cast<EncodingType>(value);
+  else if (value >= -128 && value <= 127)
+    return ENCODING_TYPE_INT8;
+  else
+    return ENCODING_TYPE_INT16;
+}
+inline constexpr EncodingType EncodeType(const uint32_t& value) {
+  if (value < (1U << 7))
+    return static_cast<EncodingType>(value);
+  else if (value < (1U << 8))
+    return ENCODING_TYPE_UINT8;
+  else if (value < (1U << 16))
+    return ENCODING_TYPE_UINT16;
+  else
+    return ENCODING_TYPE_UINT32;
+}
+inline constexpr EncodingType EncodeType(const int32_t& value) {
+  if (value >= -32 && value <= 127)
+    return static_cast<EncodingType>(value);
+  else if (value >= -128 && value <= 127)
+    return ENCODING_TYPE_INT8;
+  else if (value >= -32768 && value <= 32767)
+    return ENCODING_TYPE_INT16;
+  else
+    return ENCODING_TYPE_INT32;
+}
+inline constexpr EncodingType EncodeType(const uint64_t& value) {
+  if (value < (1ULL << 7))
+    return static_cast<EncodingType>(value);
+  else if (value < (1ULL << 8))
+    return ENCODING_TYPE_UINT8;
+  else if (value < (1ULL << 16))
+    return ENCODING_TYPE_UINT16;
+  else if (value < (1ULL << 32))
+    return ENCODING_TYPE_UINT32;
+  else
+    return ENCODING_TYPE_UINT64;
+}
+inline constexpr EncodingType EncodeType(const int64_t& value) {
+  if (value >= -32 && value <= 127)
+    return static_cast<EncodingType>(value);
+  else if (value >= -128 && value <= 127)  // Effectively [-128, -32).
+    return ENCODING_TYPE_INT8;
+  else if (value >= -32768 && value <= 32767)
+    return ENCODING_TYPE_INT16;
+  else if (value >= -2147483648 && value <= 2147483647)
+    return ENCODING_TYPE_INT32;
+  else
+    return ENCODING_TYPE_INT64;
+}
+
+inline constexpr EncodingType EncodeType(const float& /*value*/) {
+  return ENCODING_TYPE_FLOAT32;
+}
+
+inline constexpr EncodingType EncodeType(const double& /*value*/) {
+  return ENCODING_TYPE_FLOAT64;
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_ENCODING_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/enumeration.h b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
new file mode 100644
index 0000000..7a35d31
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_PDX_RPC_ENUMERATION_H_
+#define ANDROID_PDX_RPC_ENUMERATION_H_
+
+#include <pdx/rpc/sequence.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility for manipulating lists of types. Provides operations to lookup an
+// element by type or index.
+
+namespace detail {
+
+// Helper type that captures type and index for each element of a type
+// enumeration.
+template <std::size_t I, typename T>
+struct IndexedElement {
+  using Type = T;
+  static constexpr std::size_t Index = I;
+};
+
+// Helper type that captures an IndexSequence and corresponding list of types.
+template <typename Is, typename... Ts>
+struct ElementIndexer;
+
+// Partial specialization that generates an instantiation of IndexElement<I, T>
+// for each element of a type enumeration using inheritance. Once a type
+// enumeration is instantiated this way the compiler is able to deduce either I
+// or T from the other using the method below.
+template <std::size_t... Is, typename... Ts>
+struct ElementIndexer<IndexSequence<Is...>, Ts...> : IndexedElement<Is, Ts>... {
+};
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given T.
+template <typename T, std::size_t I>
+static IndexedElement<I, T> SelectElementByType(IndexedElement<I, T>);
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given I.
+template <std::size_t I, typename T>
+static IndexedElement<I, T> SelectElementByIndex(IndexedElement<I, T>);
+
+}  // namespace detail
+
+// Deduces the IndexedElement<I, T> given T and a type sequence Ts. This may be
+// used to determine the index of T within Ts at compile time.
+template <typename T, typename... Ts>
+using ElementForType = decltype(detail::SelectElementByType<T>(
+    detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+// Deduces the IndexedElement<I, T> given I and a type sequence Ts. This may be
+// used to determine the type of the element at index I within Ts at compile
+// time. Tuple operations may also be used to accomplish the same task, however
+// this implementation is provided here for symmetry.
+template <std::size_t I, typename... Ts>
+using ElementForIndex = decltype(detail::SelectElementByIndex<I>(
+    detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_ENUMERATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/find_replace.h b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
new file mode 100644
index 0000000..b4b086b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_
+#define ANDROID_PDX_RPC_FIND_REPLACE_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/copy_cv_reference.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to capture types to find and replace.
+template <typename Find, typename Replace>
+struct FindReplace;
+
+template <typename T, typename U>
+using IsSameBaseType = typename std::is_same<typename std::decay<T>::type,
+                                             typename std::decay<U>::type>;
+
+// Replaces the type Subject with type Replace if type Subject is the same type
+// as type Find, excluding cv-reference qualifiers in the match.
+template <typename Find, typename Replace, typename Subject>
+using ReplaceType =
+    typename std::conditional<IsSameBaseType<Find, Subject>::value,
+                              CopyCVReferenceType<Subject, Replace>,
+                              Subject>::type;
+
+// Determines whether the type Find (excluding cv-reference qualifiers) is in
+// the given parameter pack.
+template <typename Find, typename... Types>
+struct ContainsType : std::true_type {};
+
+template <typename Find, typename First, typename... Rest>
+struct ContainsType<Find, First, Rest...>
+    : std::conditional<IsSameBaseType<Find, First>::value, std::true_type,
+                       ContainsType<Find, Rest...>>::type {};
+
+template <typename Find>
+struct ContainsType<Find> : std::false_type {};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  //  ANDROID_PDX_RPC_FIND_REPLACE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/function_traits.h b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
new file mode 100644
index 0000000..7641b0a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
@@ -0,0 +1,61 @@
+#ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+#define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/type_operators.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type to capture return and argument types of a function signature.
+// Examples:
+//     typedef SignatureType<int(int)> SignatureType;
+//     using SignatureType = SignatureType<int(int)>;
+template <typename T>
+using SignatureType = T;
+
+// Utility class to extract return and argument types from function types.
+// Provides nested types for return value, arguments, and full signature. Also
+// provides accessor types for individual arguments, argument-arity, and type
+// substitution.
+template <typename T>
+struct FunctionTraits;
+
+template <typename Return_, typename... Args_>
+struct FunctionTraits<Return_(Args_...)> {
+  using Return = Return_;
+  using Args = std::tuple<Args_...>;
+  using Signature = SignatureType<Return_(Args_...)>;
+
+  enum : std::size_t { Arity = sizeof...(Args_) };
+
+  template <std::size_t Index>
+  using Arg = typename std::tuple_element<Index, Args>::type;
+
+  template <typename... Params>
+  using RewriteArgs =
+      SignatureType<Return_(ConditionalRewrite<Args_, Params>...)>;
+
+  template <typename ReturnType, typename... Params>
+  using RewriteSignature =
+      SignatureType<ConditionalRewrite<Return_, ReturnType>(
+          ConditionalRewrite<Args_, Params>...)>;
+
+  template <template <typename> class Wrapper, typename ReturnType,
+            typename... Params>
+  using RewriteSignatureWrapReturn =
+      SignatureType<Wrapper<ConditionalRewrite<Return_, ReturnType>>(
+          ConditionalRewrite<Args_, Params>...)>;
+
+  template <typename ReturnType>
+  using RewriteReturn =
+      SignatureType<ConditionalRewrite<Return_, ReturnType>(Args_...)>;
+};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  //  ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
new file mode 100644
index 0000000..aeae9d3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -0,0 +1,148 @@
+#ifndef ANDROID_PDX_RPC_MACROS_H_
+#define ANDROID_PDX_RPC_MACROS_H_
+
+// Macros to apply other macros over all elements in a list.
+//
+// For example, for a macro A(x) and B(x, y):
+// - FOR_EACH(A, 1, 2, 3) -> A(1) A(2) A(3).
+// - FOR_EACH_BINARY(B, z, 1, 2, 3) -> B(z, 1) B(z, 2) B(z, 3)
+// - FOR_EACH_LIST(A, 1, 2, 3) -> A(1), B(2), C(3)
+// - FOR_EACH_BINARY_LIST(B, z, 1, 2, 3) -> B(z, 1), B(z, 2), B(z, 3)
+//
+// Empty lists are supported and will produce no output.
+
+// Recursive expansion macros.
+#define _PDX_EXPAND0(...) __VA_ARGS__
+#define _PDX_EXPAND1(...) _PDX_EXPAND0(_PDX_EXPAND0(_PDX_EXPAND0(__VA_ARGS__)))
+#define _PDX_EXPAND2(...) _PDX_EXPAND1(_PDX_EXPAND1(_PDX_EXPAND1(__VA_ARGS__)))
+#define _PDX_EXPAND3(...) _PDX_EXPAND2(_PDX_EXPAND2(_PDX_EXPAND2(__VA_ARGS__)))
+#define _PDX_EXPAND4(...) _PDX_EXPAND3(_PDX_EXPAND3(_PDX_EXPAND3(__VA_ARGS__)))
+#define _PDX_EXPAND(...) _PDX_EXPAND4(_PDX_EXPAND4(_PDX_EXPAND4(__VA_ARGS__)))
+
+// Required to workaround a bug in the VC++ preprocessor.
+#define _PDX_INDIRECT_EXPAND(macro, args) macro args
+
+// Defines a step separation for macro expansion.
+#define _PDX_SEPARATOR
+
+// Clears any remaining contents wrapped in parentheses.
+#define _PDX_CLEAR(...)
+
+// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
+
+// Returns the first argument of a list.
+#define _PDX_FIRST_ARG(first, ...) first
+
+// Returns the second argument of a list.
+#define _PDX_SECOND_ARG(_, second, ...) second
+
+// Expands the arguments and introduces a separator.
+#define _PDX_EXPAND_NEXT_FUNC(_, next_func, ...)        \
+  _PDX_INDIRECT_EXPAND(_PDX_SECOND_ARG, (_, next_func)) \
+  _PDX_SEPARATOR
+
+// Returns next_func if the next element is not (), or _PDX_CLEAR
+// otherwise.
+//
+// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+#define _PDX_NEXT_FUNC(next_element, next_func) \
+  _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
+
+// Macros for the unary version of PDX_FOR_EACH.
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_1(macro, head, next, ...) \
+  macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_2(macro, head, next, ...) \
+  macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_1
+// otherwise.
+#define _PDX_HANDLE_EMPTY_ARGS(macro, ...)                    \
+  _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_1) \
+  (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH(macro, ...) \
+  _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS(macro, __VA_ARGS__))
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_1(macro, head, next, ...) \
+  , macro(head)                                   \
+        _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_2(macro, head, next, ...) \
+  , macro(head)                                   \
+        _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the start of a list.
+#define _PDX_APPLY_LIST_0(macro, head, next, ...) \
+  macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_LIST_0
+// otherwise.
+#define _PDX_HANDLE_EMPTY_LIST(macro, ...)                         \
+  _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_LIST_0) \
+  (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH_LIST(macro, ...) \
+  _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST(macro, __VA_ARGS__))
+
+// Macros for the binary version of PDX_FOR_EACH.
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_1(macro, arg, head, next, ...) \
+  macro(arg, head)                                       \
+      _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_2)(macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_2(macro, arg, head, next, ...) \
+  macro(arg, head)                                       \
+      _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_1)(macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_ARGS that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, ...)               \
+  _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_1) \
+  (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY(macro, arg, ...) \
+  _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, __VA_ARGS__))
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_1(macro, arg, head, next, ...)        \
+  , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_2)( \
+        macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_2(macro, arg, head, next, ...)        \
+  , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+        macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the start of a list. Duplicated for macro
+// recursive expansion.
+#define _PDX_APPLY_BINARY_LIST_0(macro, arg, head, next, ...)      \
+  macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+      macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_LIST that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, ...)                    \
+  _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_LIST_0) \
+  (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY_LIST(macro, arg, ...) \
+  _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, __VA_ARGS__))
+
+#endif  // ANDROID_PDX_RPC_MACROS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/message_buffer.h b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
new file mode 100644
index 0000000..ba4e86e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+#define ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+
+#include <pdx/rpc/thread_local_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type for thread-local buffers, providing suitable defaults for most
+// situations. Independent thread-local buffers may be created by using
+// different types for Slot -- ThreadLocalSlot, ThreadLocalTypedSlot and
+// ThreadLocalIndexedSlot provide utilities for building these types.
+template <typename Slot, std::size_t Capacity = 4096, typename T = std::uint8_t,
+          typename Allocator = DefaultInitializationAllocator<T>>
+using MessageBuffer = ThreadLocalBuffer<T, Allocator, Capacity, Slot>;
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/payload.h b/libs/vr/libpdx/private/pdx/rpc/payload.h
new file mode 100644
index 0000000..a48a64c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/payload.h
@@ -0,0 +1,157 @@
+#ifndef ANDROID_PDX_RPC_PAYLOAD_H_
+#define ANDROID_PDX_RPC_PAYLOAD_H_
+
+#include <iterator>
+
+#include <pdx/client.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Implements the payload interface, required by Serialize/Deserialize, on top
+// of a thread-local MessageBuffer.
+template <typename Slot>
+class MessagePayload {
+ public:
+  using BufferType = typename MessageBuffer<Slot>::BufferType;
+  using ValueType = typename MessageBuffer<Slot>::ValueType;
+
+  // Constructs a MessagePayload with an empty TLS buffer.
+  MessagePayload()
+      : buffer_(MessageBuffer<Slot>::GetEmptyBuffer()),
+        cursor_(buffer_.begin()),
+        const_cursor_(buffer_.cbegin()) {}
+
+  // Returns a reference to the cursor iterator to be used during serialization
+  // into the underlying MessageBuffer.
+  typename BufferType::iterator& Cursor() { return cursor_; }
+
+  // Returns a reference to the const cursor iterator at the beginning of the
+  // underlying MessageBuffer.
+  typename BufferType::const_iterator& ConstCursor() { return const_cursor_; }
+
+  // Returns a const iterator marking the end of the underlying MessageBuffer.
+  typename BufferType::const_iterator ConstEnd() { return buffer_.cend(); }
+
+  // Resizes the underlying MessageBuffer and sets the cursor to the beginning.
+  void Resize(std::size_t size) {
+    buffer_.resize(size);
+    cursor_ = buffer_.begin();
+    const_cursor_ = buffer_.cbegin();
+  }
+
+  // Resets the read cursor so that data can be read from the buffer again.
+  void Rewind() { const_cursor_ = buffer_.cbegin(); }
+
+  // Adds |size| bytes to the size of the underlying MessageBuffer and positions
+  // the cursor at the beginning of the extended region.
+  void Extend(std::size_t size) {
+    const std::size_t offset = buffer_.size();
+    buffer_.resize(offset + size);
+    cursor_ = buffer_.begin() + offset;
+    const_cursor_ = buffer_.cbegin() + offset;
+  }
+
+  // Clears the underlying MessageBuffer and sets the cursor to the beginning.
+  void Clear() {
+    buffer_.clear();
+    cursor_ = buffer_.begin();
+    const_cursor_ = buffer_.cbegin();
+  }
+
+  ValueType* Data() { return buffer_.data(); }
+  const ValueType* Data() const { return buffer_.data(); }
+  std::size_t Size() const { return buffer_.size(); }
+  std::size_t Capacity() const { return buffer_.capacity(); }
+
+ private:
+  BufferType& buffer_;
+  typename BufferType::iterator cursor_;
+  typename BufferType::const_iterator const_cursor_;
+
+  MessagePayload(const MessagePayload<Slot>&) = delete;
+  void operator=(const MessagePayload<Slot>&) = delete;
+};
+
+// Implements the payload interface for service-side RPCs. Handles translating
+// between remote and local handle spaces automatically.
+template <typename Slot>
+class ServicePayload : public MessagePayload<Slot>,
+                       public MessageWriter,
+                       public MessageReader {
+ public:
+  ServicePayload(Message& message) : message_(message) {}
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override {
+    this->Extend(size);
+    return &*this->Cursor();
+  }
+
+  OutputResourceMapper* GetOutputResourceMapper() override { return &message_; }
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override {
+    return {&*this->ConstCursor(), &*this->ConstEnd()};
+  }
+
+  void ConsumeReadBufferSectionData(const void* new_start) override {
+    std::advance(this->ConstCursor(),
+                 PointerDistance(new_start, &*this->ConstCursor()));
+  }
+
+  InputResourceMapper* GetInputResourceMapper() override { return &message_; }
+
+ private:
+  Message& message_;
+};
+
+// Implements the payload interface for client-side RPCs. Handles gathering file
+// handles to be sent over IPC automatically.
+template <typename Slot>
+class ClientPayload : public MessagePayload<Slot>,
+                      public MessageWriter,
+                      public MessageReader {
+ public:
+  using ContainerType =
+      MessageBuffer<ThreadLocalTypeSlot<ClientPayload<Slot>>, 1024u, int>;
+  using BufferType = typename ContainerType::BufferType;
+
+  ClientPayload(Transaction& transaction) : transaction_{transaction} {}
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override {
+    this->Extend(size);
+    return &*this->Cursor();
+  }
+
+  OutputResourceMapper* GetOutputResourceMapper() override {
+    return &transaction_;
+  }
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override {
+    return {&*this->ConstCursor(), &*this->ConstEnd()};
+  }
+
+  void ConsumeReadBufferSectionData(const void* new_start) override {
+    std::advance(this->ConstCursor(),
+                 PointerDistance(new_start, &*this->ConstCursor()));
+  }
+
+  InputResourceMapper* GetInputResourceMapper() override {
+    return &transaction_;
+  }
+
+ private:
+  Transaction& transaction_;
+};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_PAYLOAD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
new file mode 100644
index 0000000..d496719
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+#define ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for pointers to any serializable type. This class is used by
+// serialization/deserialization to handle pointers to objects that are to be
+// serialized or deserialized.
+template <typename T>
+class PointerWrapper {
+ public:
+  using BaseType = T;
+
+  PointerWrapper(T* pointer) : pointer_(pointer) {}
+  PointerWrapper(const PointerWrapper&) = default;
+  PointerWrapper(PointerWrapper&&) = default;
+  PointerWrapper& operator=(const PointerWrapper&) = default;
+  PointerWrapper& operator=(PointerWrapper&&) = default;
+
+  T& Dereference() { return *pointer_; }
+  const T& Dereference() const { return *pointer_; }
+
+ private:
+  T* pointer_;
+};
+
+template <typename T>
+PointerWrapper<T> WrapPointer(T* pointer) {
+  return PointerWrapper<T>(pointer);
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_POINTER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method.h b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
new file mode 100644
index 0000000..505c63b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
@@ -0,0 +1,473 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_H_
+
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/client.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/service.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+#ifdef __clang__
+// Stand-in type to avoid Clang compiler bug. Clang currently has a bug where
+// performing parameter pack expansion for arguments with empty packs causes a
+// compiler crash. Provide a substitute void type and specializations/overloads
+// of CheckArgumentTypes and DispatchRemoteMethod to work around this problem.
+struct Void {};
+
+// Evaluates to true if the method type is <any>(Void), false otherwise.
+template <typename RemoteMethodType>
+using IsVoidMethod = typename std::integral_constant<
+    bool, RemoteMethodType::Traits::Arity == 1 &&
+              std::is_same<typename RemoteMethodType::Traits::template Arg<0>,
+                           Void>::value>;
+
+// Utility to determine if a method is of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfVoidMethod =
+    typename std::enable_if<IsVoidMethod<RemoteMethodType>::value>::type;
+
+// Utility to determine if a method is not of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod =
+    typename std::enable_if<!IsVoidMethod<RemoteMethodType>::value>::type;
+
+#else
+// GCC works fine with void argument types, always enable the regular
+// implementation of DispatchRemoteMethod.
+using Void = void;
+template <typename RemoteMethodType>
+using EnableIfVoidMethod = void;
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod = void;
+#endif
+
+// Helper type trait to specialize InvokeRemoteMethods for return types that
+// can be obtained directly from Transaction::Send<T>() without deserializing
+// reply payload.
+template <typename T>
+struct IsDirectReturn : std::false_type {};
+
+template <>
+struct IsDirectReturn<void> : std::true_type {};
+
+template <>
+struct IsDirectReturn<int> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalHandle> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalChannelHandle> : std::true_type {};
+
+template <typename Return, typename Type = void>
+using EnableIfDirectReturn =
+    typename std::enable_if<IsDirectReturn<Return>::value, Type>::type;
+
+template <typename Return, typename Type = void>
+using EnableIfNotDirectReturn =
+    typename std::enable_if<!IsDirectReturn<Return>::value, Type>::type;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename T>
+class UnpackArguments;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename Return, typename... Args>
+class UnpackArguments<Class, Return(Args...)> {
+ public:
+  using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+  using MethodType = Return (Class::*)(Message&, Args...);
+
+  UnpackArguments(Class& instance, MethodType method, Message& message,
+                  ArgsTupleType& parameters)
+      : instance_(instance),
+        method_(method),
+        message_(message),
+        parameters_(parameters) {}
+
+  // Invokes method_ on intance_ with the packed arguments from parameters_.
+  Return Invoke() {
+    constexpr auto Arity = sizeof...(Args);
+    return static_cast<Return>(InvokeHelper(MakeIndexSequence<Arity>{}));
+  }
+
+ private:
+  Class& instance_;
+  MethodType method_;
+  Message& message_;
+  ArgsTupleType& parameters_;
+
+  template <std::size_t... Index>
+  Return InvokeHelper(IndexSequence<Index...>) {
+    return static_cast<Return>((instance_.*method_)(
+        message_,
+        std::get<Index>(std::forward<ArgsTupleType>(parameters_))...));
+  }
+
+  UnpackArguments(const UnpackArguments&) = delete;
+  void operator=(const UnpackArguments&) = delete;
+};
+
+// Returns an error code from a remote method to the client. May be called
+// either during dispatch of the remote method handler or at a later time if the
+// message is moved for delayed response.
+inline void RemoteMethodError(Message& message, int error_code) {
+  const auto status = message.ReplyError(error_code);
+  ALOGE_IF(!status, "RemoteMethodError: Failed to reply to message: %s",
+           status.GetErrorMessage().c_str());
+}
+
+// Returns a value from a remote method to the client. The usual method to
+// return a value during dispatch of a remote method is to simply use a return
+// statement in the handler. If the message is moved however, these methods may
+// be used to return a value at a later time, outside of initial dispatch.
+
+// Overload for direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+    Message& message, const Return& return_value) {
+  const auto status = message.Reply(return_value);
+  ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s",
+           status.GetErrorMessage().c_str());
+}
+
+// Overload for non-direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfNotDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+    Message& message, const Return& return_value) {
+  using Signature = typename RemoteMethodType::template RewriteReturn<Return>;
+  rpc::ServicePayload<ReplyBuffer> payload(message);
+  MakeArgumentEncoder<Signature>(&payload).EncodeReturn(return_value);
+
+  auto ret = message.WriteAll(payload.Data(), payload.Size());
+  auto status = message.Reply(ret);
+  ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s",
+           status.GetErrorMessage().c_str());
+}
+
+// Overload for Status<void> return types.
+template <typename RemoteMethodType>
+void RemoteMethodReturn(Message& message, const Status<void>& return_value) {
+  if (return_value)
+    RemoteMethodReturn<RemoteMethodType>(message, 0);
+  else
+    RemoteMethodError(message, return_value.error());
+}
+
+// Overload for Status<T> return types. This overload forwards the underlying
+// value or error within the Status<T>.
+template <typename RemoteMethodType, typename Return>
+void RemoteMethodReturn(Message& message, const Status<Return>& return_value) {
+  if (return_value)
+    RemoteMethodReturn<RemoteMethodType, Return>(message, return_value.get());
+  else
+    RemoteMethodError(message, return_value.error());
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for void return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+          typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          void (Class::*method)(Message&, Args...),
+                          Message& message,
+                          std::size_t max_capacity = InitialBufferCapacity) {
+  using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+  rpc::ServicePayload<ReceiveBuffer> payload(message);
+  payload.Resize(max_capacity);
+
+  Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+  if (!read_status) {
+    RemoteMethodError(message, read_status.error());
+    return;
+  }
+
+  payload.Resize(read_status.get());
+
+  ErrorType error;
+  auto decoder = MakeArgumentDecoder<Signature>(&payload);
+  auto arguments = decoder.DecodeArguments(&error);
+  if (error) {
+    RemoteMethodError(message, EIO);
+    return;
+  }
+
+  UnpackArguments<Class, Signature>(instance, method, message, arguments)
+      .Invoke();
+  // Return to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for generic return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+          typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          Return (Class::*method)(Message&, Args...),
+                          Message& message,
+                          std::size_t max_capacity = InitialBufferCapacity) {
+  using Signature =
+      typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+  rpc::ServicePayload<ReceiveBuffer> payload(message);
+  payload.Resize(max_capacity);
+
+  Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+  if (!read_status) {
+    RemoteMethodError(message, read_status.error());
+    return;
+  }
+
+  payload.Resize(read_status.get());
+
+  ErrorType error;
+  auto decoder = MakeArgumentDecoder<Signature>(&payload);
+  auto arguments = decoder.DecodeArguments(&error);
+  if (error) {
+    RemoteMethodError(message, EIO);
+    return;
+  }
+
+  auto return_value =
+      UnpackArguments<Class, Signature>(instance, method, message, arguments)
+          .Invoke();
+  // Return the value to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for Status<T> return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+          typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          Status<Return> (Class::*method)(Message&, Args...),
+                          Message& message,
+                          std::size_t max_capacity = InitialBufferCapacity) {
+  using Signature =
+      typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+  using InvokeSignature =
+      typename RemoteMethodType::template RewriteSignatureWrapReturn<
+          Status, Return, Args...>;
+  rpc::ServicePayload<ReceiveBuffer> payload(message);
+  payload.Resize(max_capacity);
+
+  Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+  if (!read_status) {
+    RemoteMethodError(message, read_status.error());
+    return;
+  }
+
+  payload.Resize(read_status.get());
+
+  ErrorType error;
+  auto decoder = MakeArgumentDecoder<Signature>(&payload);
+  auto arguments = decoder.DecodeArguments(&error);
+  if (error) {
+    RemoteMethodError(message, EIO);
+    return;
+  }
+
+  auto return_value = UnpackArguments<Class, InvokeSignature>(
+                          instance, method, message, arguments)
+                          .Invoke();
+  // Return the value to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+#ifdef __clang__
+// Overloads to handle Void argument type without exploding clang.
+
+// Overload for void return type.
+template <typename RemoteMethodType, typename Class,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&),
+                          Message& message) {
+  (instance.*method)(message);
+  // Return to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Overload for generic return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&),
+                          Message& message) {
+  auto return_value = (instance.*method)(message);
+  // Return the value to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for Status<T> return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          Status<Return> (Class::*method)(Message&),
+                          Message& message) {
+  auto return_value = (instance.*method)(message);
+  // Return the value to the caller unless the message was moved.
+  if (message)
+    RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+#endif
+
+}  // namespace rpc
+
+// Definitions for template methods declared in pdx/client.h.
+
+template <int Opcode, typename T>
+struct CheckArgumentTypes;
+
+template <int Opcode, typename Return, typename... Args>
+struct CheckArgumentTypes<Opcode, Return(Args...)> {
+  template <typename R>
+  static typename rpc::EnableIfDirectReturn<R, Status<R>> Invoke(Client& client,
+                                                                 Args... args) {
+    Transaction trans{client};
+    rpc::ClientPayload<rpc::SendBuffer> payload{trans};
+    rpc::MakeArgumentEncoder<Return(Args...)>(&payload).EncodeArguments(
+        std::forward<Args>(args)...);
+    return trans.Send<R>(Opcode, payload.Data(), payload.Size(), nullptr, 0);
+  }
+
+  template <typename R>
+  static typename rpc::EnableIfNotDirectReturn<R, Status<R>> Invoke(
+      Client& client, Args... args) {
+    Transaction trans{client};
+
+    rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+    rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+        .EncodeArguments(std::forward<Args>(args)...);
+
+    rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+    reply_payload.Resize(reply_payload.Capacity());
+
+    Status<R> result;
+    auto status =
+        trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+                         reply_payload.Data(), reply_payload.Size());
+    if (!status) {
+      result.SetError(status.error());
+    } else {
+      R return_value;
+      rpc::ErrorType error =
+          rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+              .DecodeReturn(&return_value);
+
+      switch (error.error_code()) {
+        case rpc::ErrorCode::NO_ERROR:
+          result.SetValue(std::move(return_value));
+          break;
+
+        // This error is returned when ArrayWrapper/StringWrapper is too
+        // small to receive the payload.
+        case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+          result.SetError(ENOBUFS);
+          break;
+
+        default:
+          result.SetError(EIO);
+          break;
+      }
+    }
+    return result;
+  }
+
+  template <typename R>
+  static typename rpc::EnableIfDirectReturn<R, Status<void>> InvokeInPlace(
+      Client& client, R* return_value, Args... args) {
+    Transaction trans{client};
+
+    rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+    rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+        .EncodeArguments(std::forward<Args>(args)...);
+
+    Status<void> result;
+    auto status = trans.Send<R>(Opcode, send_payload.Data(),
+                                send_payload.Size(), nullptr, 0);
+    if (status) {
+      *return_value = status.take();
+      result.SetValue();
+    } else {
+      result.SetError(status.error());
+    }
+    return result;
+  }
+
+  template <typename R>
+  static typename rpc::EnableIfNotDirectReturn<R, Status<void>> InvokeInPlace(
+      Client& client, R* return_value, Args... args) {
+    Transaction trans{client};
+
+    rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+    rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+        .EncodeArguments(std::forward<Args>(args)...);
+
+    rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+    reply_payload.Resize(reply_payload.Capacity());
+
+    auto result =
+        trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+                         reply_payload.Data(), reply_payload.Size());
+    if (result) {
+      rpc::ErrorType error =
+          rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+              .DecodeReturn(return_value);
+
+      switch (error.error_code()) {
+        case rpc::ErrorCode::NO_ERROR:
+          result.SetValue();
+          break;
+
+        // This error is returned when ArrayWrapper/StringWrapper is too
+        // small to receive the payload.
+        case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+          result.SetError(ENOBUFS);
+          break;
+
+        default:
+          result.SetError(EIO);
+          break;
+      }
+    }
+    return result;
+  }
+};
+
+// Invokes the remote method with opcode and signature described by
+// |RemoteMethodType|.
+template <typename RemoteMethodType, typename... Args>
+Status<typename RemoteMethodType::Return> Client::InvokeRemoteMethod(
+    Args&&... args) {
+  return CheckArgumentTypes<
+      RemoteMethodType::Opcode,
+      typename RemoteMethodType::template RewriteArgs<Args...>>::
+      template Invoke<typename RemoteMethodType::Return>(
+          *this, std::forward<Args>(args)...);
+}
+
+template <typename RemoteMethodType, typename Return, typename... Args>
+Status<void> Client::InvokeRemoteMethodInPlace(Return* return_value,
+                                               Args&&... args) {
+  return CheckArgumentTypes<
+      RemoteMethodType::Opcode,
+      typename RemoteMethodType::template RewriteSignature<Return, Args...>>::
+      template InvokeInPlace(*this, return_value, std::forward<Args>(args)...);
+}
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_REMOTE_METHOD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
new file mode 100644
index 0000000..cf9a189
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/enumeration.h>
+#include <pdx/rpc/function_traits.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class binding a remote method opcode to its function signature.
+// Describes the interface between RPC clients and services for a single method.
+template <int Opcode_, typename Signature_>
+struct RemoteMethodType {
+  typedef FunctionTraits<Signature_> Traits;
+
+  enum : int { Opcode = Opcode_ };
+
+  typedef typename Traits::Signature Signature;
+  typedef typename Traits::Return Return;
+  typedef typename Traits::Args Args;
+
+  template <typename... Params>
+  using RewriteArgs = typename Traits::template RewriteArgs<Params...>;
+
+  template <typename ReturnType, typename... Params>
+  using RewriteSignature =
+      typename Traits::template RewriteSignature<ReturnType, Params...>;
+
+  template <template <typename> class Wrapper, typename ReturnType,
+            typename... Params>
+  using RewriteSignatureWrapReturn =
+      typename Traits::template RewriteSignatureWrapReturn<Wrapper, ReturnType,
+                                                           Params...>;
+
+  template <typename ReturnType>
+  using RewriteReturn = typename Traits::template RewriteReturn<ReturnType>;
+};
+
+// Utility class representing a set of related RemoteMethodTypes. Describes the
+// interface between RPC clients and services as a set of methods.
+template <typename... MethodTypes>
+struct RemoteAPI {
+  typedef std::tuple<MethodTypes...> Methods;
+  enum : std::size_t { Length = sizeof...(MethodTypes) };
+
+  template <std::size_t Index>
+  using Method = typename std::tuple_element<Index, Methods>::type;
+
+  template <typename MethodType>
+  static constexpr std::size_t MethodIndex() {
+    return ElementForType<MethodType, MethodTypes...>::Index;
+  }
+};
+
+// Macro to simplify defining remote method signatures. Remote method signatures
+// are specified by defining a RemoteMethodType for each remote method.
+#define PDX_REMOTE_METHOD(name, opcode, ... /*signature*/) \
+  using name = ::android::pdx::rpc::RemoteMethodType<opcode, __VA_ARGS__>
+
+// Macro to simplify defining a set of remote method signatures.
+#define PDX_REMOTE_API(name, ... /*methods*/) \
+  using name = ::android::pdx::rpc::RemoteAPI<__VA_ARGS__>
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/sequence.h b/libs/vr/libpdx/private/pdx/rpc/sequence.h
new file mode 100644
index 0000000..5fd898a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/sequence.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_RPC_SEQUENCE_H_
+#define ANDROID_PDX_RPC_SEQUENCE_H_
+
+#include <cstdint>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides a C++11 implementation of C++14 index_sequence and
+// make_index_sequence for compatibility with common compilers. This
+// implementation may be conditionally replaced with compiler-provided versions
+// when C++14 support is available.
+
+// Utility to capture a sequence of unsigned indices.
+template <std::size_t... I>
+struct IndexSequence {
+  using type = IndexSequence;
+  using value_type = std::size_t;
+  static constexpr std::size_t size() { return sizeof...(I); }
+};
+
+namespace detail {
+
+// Helper class to merge and renumber sequence parts in log N instantiations.
+template <typename S1, typename S2>
+struct MergeSequencesAndRenumber;
+
+template <std::size_t... I1, std::size_t... I2>
+struct MergeSequencesAndRenumber<IndexSequence<I1...>, IndexSequence<I2...>>
+    : IndexSequence<I1..., (sizeof...(I1) + I2)...> {};
+
+}  // namespace detail
+
+// Utility to build an IndexSequence with N indices.
+template <std::size_t N>
+struct MakeIndexSequence : detail::MergeSequencesAndRenumber<
+                               typename MakeIndexSequence<N / 2>::type,
+                               typename MakeIndexSequence<N - N / 2>::type> {};
+
+// Identity sequences.
+template <>
+struct MakeIndexSequence<0> : IndexSequence<> {};
+template <>
+struct MakeIndexSequence<1> : IndexSequence<0> {};
+
+// Utility to build an IndexSequence with indices for each element of a
+// parameter pack.
+template <typename... T>
+using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>;
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_SEQUENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serializable.h b/libs/vr/libpdx/private/pdx/rpc/serializable.h
new file mode 100644
index 0000000..04a4352
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serializable.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZABLE_H_
+#define ANDROID_PDX_RPC_SERIALIZABLE_H_
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+
+#include "macros.h"
+#include "serialization.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This file provides utilities to define serializable types for communication
+// between clients and services. Supporting efficient, typed communication
+// protocols is the primary goal, NOT providing a general-purpose solution for
+// all your C++ serialization needs. Features that are not aligned to the goals
+// are not supported, such as static/const member serialization and serializable
+// types with virtual methods (requiring a virtual destructor).
+
+// Captures the type and value of a pointer to member. Pointer to members are
+// essentially compile-time constant offsets that can be stored in the type
+// system without adding to the size of the structures they describe. This
+// library uses this property to implement a limited form of reflection for
+// serialization/deserialization functions.
+template <typename T, T>
+struct MemberPointer;
+
+template <typename Type, typename Class, Type Class::*Pointer>
+struct MemberPointer<Type Class::*, Pointer> {
+  // Type of the member pointer this type represents.
+  using PointerType = Type Class::*;
+
+  // Resolves a pointer to member with the given instance, yielding a
+  // reference to the member in that instance.
+  static Type& Resolve(Class& instance) { return (instance.*Pointer); }
+  static const Type& Resolve(const Class& instance) {
+    return (instance.*Pointer);
+  }
+};
+
+// Describes a set of members to be serialized/deserialized by this library. The
+// parameter pack MemberPointers takes a list of MemberPointer types that
+// describe each member to participate in serialization/deserialization.
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType {
+  using Type = T;
+
+  // The number of member pointers described by this type.
+  enum : std::size_t { MemberCount = sizeof...(MemberPointers) };
+
+  // The member pointers described by this type.
+  using Members = std::tuple<MemberPointers...>;
+
+  // Accessor for individual member pointer types.
+  template <std::size_t Index>
+  using At = typename std::tuple_element<Index, Members>::type;
+};
+
+// Classes must do the following to correctly define a serializable type:
+//     1. Define a type called "SerializableMembers" as a template instantiation
+//        of SerializableMembersType, describing the members of the class to
+//        participate in serialization (presumably all of them). Use the macro
+//        PDX_SERIALIZABLE_MEMBERS(...) below to aid the correct type
+//        definition. This type should be private to prevent leaking member
+//        access information.
+//     2. Make SerializableTraits and HasSerilizableMembers types a friend of
+//        the class. The macro PDX_SERIALIZABLE_MEMEBRS(...) takes care of
+//        this automatically.
+//     3. Define a public default constructor, if necessary. Deserialization
+//        requires instances to be default-constructible.
+//
+// Example usage:
+//     class MySerializableType : public AnotherBaseType {
+//      public:
+//       MySerializableType();
+//       ...
+//      private:
+//       int a;
+//       string b;
+//       PDX_SERIALIZABLE_MEMBERS(MySerializableType, a, b);
+//     };
+//
+// Note that const and static member serialization is not supported.
+
+template <typename T>
+class SerializableTraits {
+ public:
+  // Gets the serialized size of type T.
+  static std::size_t GetSerializedSize(const T& value) {
+    return GetEncodingSize(EncodeArrayType(SerializableMembers::MemberCount)) +
+           GetMembersSize<SerializableMembers>(value);
+  }
+
+  // Serializes type T.
+  static void SerializeObject(const T& value, MessageWriter* writer,
+                              void*& buffer) {
+    SerializeArrayEncoding(EncodeArrayType(SerializableMembers::MemberCount),
+                           SerializableMembers::MemberCount, buffer);
+    SerializeMembers<SerializableMembers>(value, writer, buffer);
+  }
+
+  // Deserializes type T.
+  static ErrorType DeserializeObject(T* value, MessageReader* reader,
+                                     const void*& start, const void* end) {
+    EncodingType encoding;
+    std::size_t size;
+
+    if (const auto error =
+            DeserializeArrayType(&encoding, &size, reader, start, end)) {
+      return error;
+    } else if (size != SerializableMembers::MemberCount) {
+      return ErrorCode::UNEXPECTED_TYPE_SIZE;
+    } else {
+      return DeserializeMembers<SerializableMembers>(value, reader, start, end);
+    }
+  }
+
+ private:
+  using SerializableMembers = typename T::SerializableMembers;
+};
+
+// Utility macro to define a MemberPointer type for a member name.
+#define PDX_MEMBER_POINTER(type, member) \
+  ::android::pdx::rpc::MemberPointer<decltype(&type::member), &type::member>
+
+// Defines a list of MemberPointer types given a list of member names.
+#define PDX_MEMBERS(type, ... /*members*/) \
+  PDX_FOR_EACH_BINARY_LIST(PDX_MEMBER_POINTER, type, __VA_ARGS__)
+
+// Defines the serializable members of a type given a list of member names and
+// befriends SerializableTraits and HasSerializableMembers for the class. This
+// macro handles requirements #1 and #2 above.
+#define PDX_SERIALIZABLE_MEMBERS(type, ... /*members*/)                     \
+  template <typename T>                                                     \
+  friend class ::android::pdx::rpc::SerializableTraits;                     \
+  template <typename, typename>                                             \
+  friend struct ::android::pdx::rpc::HasSerializableMembers;                \
+  using SerializableMembers = ::android::pdx::rpc::SerializableMembersType< \
+      type, PDX_MEMBERS(type, __VA_ARGS__)>
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_SERIALIZABLE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serialization.h b/libs/vr/libpdx/private/pdx/rpc/serialization.h
new file mode 100644
index 0000000..f12aef1
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h
@@ -0,0 +1,1998 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZATION_H_
+#define ANDROID_PDX_RPC_SERIALIZATION_H_
+
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/trace.h>
+#include <pdx/utility.h>
+
+#include "array_wrapper.h"
+#include "default_initialization_allocator.h"
+#include "encoding.h"
+#include "pointer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Automatic serialization/deserialization library based on MessagePack
+// (http://msgpack.org). This library provides top level Serialize() and
+// Deserialize() functions to encode/decode a variety of data types.
+//
+// The following data types are supported:
+//   * Standard signed integer types: int8_t, int16_t, int32_t, and int64_t.
+//   * Regular signed integer types equivalent to the standard types:
+//     signed char, short, int, long, and long long.
+//   * Standard unsigned integer types: uint8_t, uint16_t, uint32_t, and
+//     uint64_t.
+//   * Regular unsigned integer types equivalent to the standard types:
+//     unsigned char, unsigned short, unsigned int, unsigned long,
+//     and unsigned long long.
+//   * char without signed/unsigned qualifiers.
+//   * bool.
+//   * std::vector with value type of any supported type, including nesting.
+//   * std::string.
+//   * std::tuple with elements of any supported type, including nesting.
+//   * std::pair with elements of any supported type, including nesting.
+//   * std::map with keys and values of any supported type, including nesting.
+//   * std::unordered_map with keys and values of any supported type, including
+//     nesting.
+//   * std::array with values of any supported type, including nesting.
+//   * ArrayWrapper of any supported basic type.
+//   * BufferWrapper of any POD type.
+//   * StringWrapper of any supported char type.
+//   * User types with correctly defined SerializableMembers member type.
+//
+// Planned support for:
+//   * std::basic_string with all supported char types.
+
+// Counting template for managing template recursion.
+template <std::size_t N>
+struct Index {};
+
+// Forward declaration of traits type to access types with a SerializedMembers
+// member type.
+template <typename T>
+class SerializableTraits;
+
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType;
+
+// Utility to deduce the template type from a derived type.
+template <template <typename...> class TT, typename... Ts>
+std::true_type DeduceTemplateType(const TT<Ts...>*);
+template <template <typename...> class TT>
+std::false_type DeduceTemplateType(...);
+
+// Utility determining whether template type TT<...> is a base of type T.
+template <template <typename...> class TT, typename T>
+using IsTemplateBaseOf = decltype(DeduceTemplateType<TT>(std::declval<T*>()));
+
+// Utility type for SFINAE in HasHasSerializableMembers.
+template <typename... Ts>
+using TrySerializableMembersType = void;
+
+// Determines whether type T has a member type named SerializableMembers of
+// template type SerializableMembersType.
+template <typename, typename = void>
+struct HasSerializableMembers : std::false_type {};
+template <typename T>
+struct HasSerializableMembers<
+    T, TrySerializableMembersType<typename T::SerializableMembers>>
+    : std::integral_constant<
+          bool, IsTemplateBaseOf<SerializableMembersType,
+                                 typename T::SerializableMembers>::value> {};
+
+// Utility to simplify overload enable expressions for types with correctly
+// defined SerializableMembers.
+template <typename T>
+using EnableIfHasSerializableMembers =
+    typename std::enable_if<HasSerializableMembers<T>::value>::type;
+
+// Utility to simplify overload enable expressions for enum types.
+template <typename T, typename ReturnType = void>
+using EnableIfEnum =
+    typename std::enable_if<std::is_enum<T>::value, ReturnType>::type;
+
+///////////////////////////////////////////////////////////////////////////////
+// Error Reporting //
+///////////////////////////////////////////////////////////////////////////////
+
+// Error codes returned by the deserialization code.
+enum class ErrorCode {
+  NO_ERROR = 0,
+  UNEXPECTED_ENCODING,
+  UNEXPECTED_TYPE_SIZE,
+  INSUFFICIENT_BUFFER,
+  INSUFFICIENT_DESTINATION_SIZE,
+  GET_FILE_DESCRIPTOR_FAILED,
+  GET_CHANNEL_HANDLE_FAILED,
+  INVALID_VARIANT_ELEMENT,
+};
+
+// Type for errors returned by the deserialization code.
+class ErrorType {
+ public:
+  ErrorType() : error_code_(ErrorCode::NO_ERROR) {}
+
+  // ErrorType constructor for generic error codes. Explicitly not explicit,
+  // implicit conversion from ErrorCode to ErrorType is desirable behavior.
+  // NOLINTNEXTLINE(runtime/explicit)
+  ErrorType(ErrorCode error_code) : error_code_(error_code) {}
+
+  // ErrorType constructor for encoding type errors.
+  ErrorType(ErrorCode error_code, EncodingClass encoding_class,
+            EncodingType encoding_type)
+      : error_code_(error_code) {
+    unexpected_encoding_.encoding_class = encoding_class;
+    unexpected_encoding_.encoding_type = encoding_type;
+  }
+
+  // Evaluates to true if the ErrorType represents an error.
+  explicit operator bool() const { return error_code_ != ErrorCode::NO_ERROR; }
+
+  operator ErrorCode() const { return error_code_; }
+  ErrorCode error_code() const { return error_code_; }
+
+  // Accessors for extra info about unexpected encoding errors.
+  EncodingClass encoding_class() const {
+    return unexpected_encoding_.encoding_class;
+  }
+  EncodingType encoding_type() const {
+    return unexpected_encoding_.encoding_type;
+  }
+
+  operator std::string() const {
+    std::ostringstream stream;
+
+    switch (error_code_) {
+      case ErrorCode::NO_ERROR:
+        return "NO_ERROR";
+      case ErrorCode::UNEXPECTED_ENCODING:
+        stream << "UNEXPECTED_ENCODING: " << static_cast<int>(encoding_class())
+               << ", " << static_cast<int>(encoding_type());
+        return stream.str();
+      case ErrorCode::UNEXPECTED_TYPE_SIZE:
+        return "UNEXPECTED_TYPE_SIZE";
+      case ErrorCode::INSUFFICIENT_BUFFER:
+        return "INSUFFICIENT_BUFFER";
+      case ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+        return "INSUFFICIENT_DESTINATION_SIZE";
+      default:
+        return "[Unknown Error]";
+    }
+  }
+
+ private:
+  ErrorCode error_code_;
+
+  // Union of extra information for different error code types.
+  union {
+    // UNEXPECTED_ENCODING.
+    struct {
+      EncodingClass encoding_class;
+      EncodingType encoding_type;
+    } unexpected_encoding_;
+  };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Size //
+///////////////////////////////////////////////////////////////////////////////
+
+inline constexpr std::size_t GetSerializedSize(const bool& b) {
+  return GetEncodingSize(EncodeType(b));
+}
+
+// Overloads of GetSerializedSize() for standard integer types.
+inline constexpr std::size_t GetSerializedSize(const char& c) {
+  return GetEncodingSize(EncodeType(c));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint8_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int8_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint16_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int16_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint32_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int32_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint64_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int64_t& i) {
+  return GetEncodingSize(EncodeType(i));
+}
+
+inline constexpr std::size_t GetSerializedSize(const float& f) {
+  return GetEncodingSize(EncodeType(f));
+}
+inline constexpr std::size_t GetSerializedSize(const double& d) {
+  return GetEncodingSize(EncodeType(d));
+}
+
+// Overload for enum types.
+template <typename T>
+inline EnableIfEnum<T, std::size_t> GetSerializedSize(T v) {
+  return GetSerializedSize(static_cast<std::underlying_type_t<T>>(v));
+}
+
+// Forward declaration for nested definitions.
+inline std::size_t GetSerializedSize(const EmptyVariant&);
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>&);
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline constexpr std::size_t GetSerializedSize(const T&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>&);
+inline constexpr std::size_t GetSerializedSize(const std::string&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>&);
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>&);
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const ChannelHandle<Mode>&);
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+    const std::map<Key, T, Compare, Allocator>& m);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline std::size_t GetSerializedSize(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&);
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>&);
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v);
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p);
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple);
+
+// Overload for empty variant type.
+inline std::size_t GetSerializedSize(const EmptyVariant& empty) {
+  return GetEncodingSize(EncodeType(empty));
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>& variant) {
+  return GetEncodingSize(EncodeType(variant)) +
+         GetSerializedSize(variant.index()) +
+         variant.Visit(
+             [](const auto& value) { return GetSerializedSize(value); });
+}
+
+// Overload for structs/classes with SerializableMembers defined.
+template <typename T, typename Enabled>
+inline constexpr std::size_t GetSerializedSize(const T& value) {
+  return SerializableTraits<T>::GetSerializedSize(value);
+}
+
+// Overload for PointerWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>& p) {
+  return GetSerializedSize(p.Dereference());
+}
+
+// Overload for std::string.
+inline constexpr std::size_t GetSerializedSize(const std::string& s) {
+  return GetEncodingSize(EncodeType(s)) +
+         s.length() * sizeof(std::string::value_type);
+}
+
+// Overload for StringWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>& s) {
+  return GetEncodingSize(EncodeType(s)) +
+         s.length() * sizeof(typename StringWrapper<T>::value_type);
+}
+
+// Overload for BufferWrapper types.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>& b) {
+  return GetEncodingSize(EncodeType(b)) +
+         b.size() * sizeof(typename BufferWrapper<T>::value_type);
+}
+
+// Overload for FileHandle. FileHandle is encoded as a FIXEXT2, with a type code
+// of "FileHandle" and a signed 16-bit offset into the pushed fd array. Empty
+// FileHandles are encoded with an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>& fd) {
+  return GetEncodingSize(EncodeType(fd)) + sizeof(std::int16_t);
+}
+
+// Overload for ChannelHandle. ChannelHandle is encoded as a FIXEXT4, with a
+// type code of "ChannelHandle" and a signed 32-bit offset into the pushed
+// channel array. Empty ChannelHandles are encoded with an array index of -1.
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(
+    const ChannelHandle<Mode>& channel_handle) {
+  return GetEncodingSize(EncodeType(channel_handle)) + sizeof(std::int32_t);
+}
+
+// Overload for standard vector types.
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v) {
+  return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+                         [](const std::size_t& sum, const T& object) {
+                           return sum + GetSerializedSize(object);
+                         });
+}
+
+// Overload for standard map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+    const std::map<Key, T, Compare, Allocator>& v) {
+  return std::accumulate(
+      v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+      [](const std::size_t& sum, const std::pair<Key, T>& object) {
+        return sum + GetSerializedSize(object.first) +
+               GetSerializedSize(object.second);
+      });
+}
+
+// Overload for standard unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline std::size_t GetSerializedSize(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
+  return std::accumulate(
+      v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+      [](const std::size_t& sum, const std::pair<Key, T>& object) {
+        return sum + GetSerializedSize(object.first) +
+               GetSerializedSize(object.second);
+      });
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>& v) {
+  return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+                         [](const std::size_t& sum, const T& object) {
+                           return sum + GetSerializedSize(object);
+                         });
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v) {
+  return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+                         [](const std::size_t& sum, const T& object) {
+                           return sum + GetSerializedSize(object);
+                         });
+}
+
+// Overload for std::pair.
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p) {
+  return GetEncodingSize(EncodeType(p)) + GetSerializedSize(p.first) +
+         GetSerializedSize(p.second);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline std::size_t GetTupleSize(const std::tuple<T...>&, Index<0>) {
+  return 0;
+}
+
+// Gets the size of each element in a tuple recursively.
+template <typename... T, std::size_t index>
+inline std::size_t GetTupleSize(const std::tuple<T...>& tuple, Index<index>) {
+  return GetTupleSize(tuple, Index<index - 1>()) +
+         GetSerializedSize(std::get<index - 1>(tuple));
+}
+
+// Overload for tuple types. Gets the size of the tuple, recursing
+// through the elements.
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple) {
+  return GetEncodingSize(EncodeType(tuple)) +
+         GetTupleSize(tuple, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member of a Serializable
+// type is reached.
+template <typename Members, typename T>
+inline std::size_t GetMemberSize(const T&, Index<0>) {
+  return 0;
+}
+
+// Gets the size of each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline std::size_t GetMemberSize(const T& object, Index<index>) {
+  return GetMemberSize<Members>(object, Index<index - 1>()) +
+         GetSerializedSize(Members::template At<index - 1>::Resolve(object));
+}
+
+// Gets the size of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline std::size_t GetMembersSize(const T& object) {
+  return GetMemberSize<Members>(object, Index<Members::MemberCount>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Serialization //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// SerializeRaw() converts a primitive array or type into a raw byte string.
+// These functions are named differently from SerializeObject() expressly to
+// avoid catch-all specialization of that template, which can be difficult to
+// detect otherwise.
+//
+
+inline void WriteRawData(void*& dest, const void* src, size_t size) {
+  memcpy(dest, src, size);
+  dest = static_cast<uint8_t*>(dest) + size;
+}
+
+// Serializes a primitive array into a raw byte string.
+template <typename T,
+          typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline void SerializeRaw(const T& value, void*& buffer) {
+  WriteRawData(buffer, &value, sizeof(value));
+}
+
+inline void SerializeEncoding(EncodingType encoding, void*& buffer) {
+  SerializeRaw(encoding, buffer);
+}
+
+inline void SerializeType(const bool& value, void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code, extended type code, and size for
+// extension types.
+inline void SerializeExtEncoding(EncodingType encoding,
+                                 EncodingExtType ext_type, std::size_t size,
+                                 void*& buffer) {
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_EXT8) {
+    std::uint8_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_EXT16) {
+    std::uint16_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_EXT32) {
+    std::uint32_t length = size;
+    SerializeRaw(length, buffer);
+  } else /* if (IsFixextEncoding(encoding) */ {
+    // Encoding byte contains the fixext length, nothing else to do.
+  }
+  SerializeRaw(ext_type, buffer);
+}
+
+// Serializes the type code for file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeType(const FileHandle<Mode>& value, void*& buffer) {
+  SerializeExtEncoding(EncodeType(value), ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 2,
+                       buffer);
+}
+
+// Serializes the type code for channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeType(const ChannelHandle<Mode>& handle, void*& buffer) {
+  SerializeExtEncoding(EncodeType(handle), ENCODING_EXT_TYPE_CHANNEL_HANDLE, 4,
+                       buffer);
+}
+
+// Serializes type code for variant types.
+template <typename... Types>
+inline void SerializeType(const Variant<Types...>& value, void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code for string types.
+template <typename StringType>
+inline void SerializeStringType(const StringType& value, void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_STR8) {
+    std::uint8_t length = value.length();
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_STR16) {
+    std::uint16_t length = value.length();
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_STR32) {
+    std::uint32_t length = value.length();
+    SerializeRaw(length, buffer);
+  } else /* if (IsFixstrEncoding(encoding) */ {
+    // Encoding byte contains the fixstr length, nothing else to do.
+  }
+}
+
+// Serializes the type code for std::string and StringWrapper. These types are
+// interchangeable and must serialize to the same format.
+inline void SerializeType(const std::string& value, void*& buffer) {
+  SerializeStringType(value, buffer);
+}
+template <typename T>
+inline void SerializeType(const StringWrapper<T>& value, void*& buffer) {
+  SerializeStringType(value, buffer);
+}
+
+// Serializes the type code for bin types.
+inline void SerializeBinEncoding(EncodingType encoding, std::size_t size,
+                                 void*& buffer) {
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_BIN8) {
+    std::uint8_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_BIN16) {
+    std::uint16_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_BIN32) {
+    std::uint32_t length = size;
+    SerializeRaw(length, buffer);
+  } else {
+    // Invalid encoding for BIN type.
+  }
+}
+
+// Serializes the type code for BufferWrapper types.
+template <typename T>
+inline void SerializeType(const BufferWrapper<T>& value, void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeBinEncoding(
+      encoding, value.size() * sizeof(typename BufferWrapper<T>::value_type),
+      buffer);
+}
+
+// Serializes the array encoding type and length.
+inline void SerializeArrayEncoding(EncodingType encoding, std::size_t size,
+                                   void*& buffer) {
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_ARRAY16) {
+    std::uint16_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_ARRAY32) {
+    std::uint32_t length = size;
+    SerializeRaw(length, buffer);
+  } else /* if (IsFixarrayEncoding(encoding) */ {
+    // Encoding byte contains the fixarray length, nothing else to do.
+  }
+}
+
+// Serializes the map encoding type and length.
+inline void SerializeMapEncoding(EncodingType encoding, std::size_t size,
+                                 void*& buffer) {
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_MAP16) {
+    std::uint16_t length = size;
+    SerializeRaw(length, buffer);
+  } else if (encoding == ENCODING_TYPE_MAP32) {
+    std::uint32_t length = size;
+    SerializeRaw(length, buffer);
+  } else /* if (IsFixmapEncoding(encoding) */ {
+    // Encoding byte contains the fixmap length, nothing else to do.
+  }
+}
+
+// Serializes the type code for array types.
+template <typename ArrayType>
+inline void SerializeArrayType(const ArrayType& value, std::size_t size,
+                               void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeArrayEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for map types.
+template <typename MapType>
+inline void SerializeMapType(const MapType& value, std::size_t size,
+                             void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeMapEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for std::vector and ArrayWrapper. These types are
+// interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeType(const std::vector<T, Allocator>& value,
+                          void*& buffer) {
+  SerializeArrayType(value, value.size(), buffer);
+}
+template <typename T>
+inline void SerializeType(const ArrayWrapper<T>& value, void*& buffer) {
+  SerializeArrayType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::array. This type serializes to the same
+// format as std::vector and ArrayWrapper and is interchangeable in certain
+// situations.
+template <typename T, std::size_t Size>
+inline void SerializeType(const std::array<T, Size>& value, void*& buffer) {
+  SerializeArrayType(value, Size, buffer);
+}
+
+// Serializes the type code for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeType(const std::map<Key, T, Compare, Allocator>& value,
+                          void*& buffer) {
+  SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline void SerializeType(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value,
+    void*& buffer) {
+  SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::pair types.
+template <typename T, typename U>
+inline void SerializeType(const std::pair<T, U>& value, void*& buffer) {
+  SerializeArrayType(value, 2, buffer);
+}
+
+// Serializes the type code for std::tuple types.
+template <typename... T>
+inline void SerializeType(const std::tuple<T...>& value, void*& buffer) {
+  SerializeArrayType(value, sizeof...(T), buffer);
+}
+
+// Specialization of SerializeObject for boolean type.
+inline void SerializeObject(const bool& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  SerializeType(value, buffer);
+  // Encoding contains the boolean value, nothing else to do.
+}
+
+// Overloads of SerializeObject for float and double types.
+inline void SerializeObject(const float& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  SerializeRaw(value, buffer);
+}
+
+inline void SerializeObject(const double& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  SerializeRaw(value, buffer);
+}
+
+// Overloads of SerializeObject() for standard integer types.
+inline void SerializeObject(const char& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_UINT8) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const int8_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_INT8) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const uint8_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_UINT8) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const int16_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_INT8) {
+    const int8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_INT16) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const uint16_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_UINT8) {
+    const uint8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT16) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const int32_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_INT8) {
+    const int8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_INT16) {
+    const int16_t half = value;
+    SerializeRaw(half, buffer);
+  } else if (encoding == ENCODING_TYPE_INT32) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const uint32_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_UINT8) {
+    const uint8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT16) {
+    const uint16_t half = value;
+    SerializeRaw(half, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT32) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const int64_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_INT8) {
+    const int8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_INT16) {
+    const int16_t half = value;
+    SerializeRaw(half, buffer);
+  } else if (encoding == ENCODING_TYPE_INT32) {
+    const int32_t word = value;
+    SerializeRaw(word, buffer);
+  } else if (encoding == ENCODING_TYPE_INT64) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+inline void SerializeObject(const uint64_t& value, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  const EncodingType encoding = EncodeType(value);
+  SerializeEncoding(encoding, buffer);
+  if (encoding == ENCODING_TYPE_UINT8) {
+    const uint8_t byte = value;
+    SerializeRaw(byte, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT16) {
+    const uint16_t half = value;
+    SerializeRaw(half, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT32) {
+    const uint32_t word = value;
+    SerializeRaw(word, buffer);
+  } else if (encoding == ENCODING_TYPE_UINT64) {
+    SerializeRaw(value, buffer);
+  } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+    // Encoding byte contains the value, nothing else to do.
+  }
+}
+
+// Serialize enum types.
+template <typename T>
+inline EnableIfEnum<T> SerializeObject(const T& value, MessageWriter* writer,
+                                       void*& buffer) {
+  SerializeObject(static_cast<std::underlying_type_t<T>>(value), writer,
+                  buffer);
+}
+
+// Forward declaration for nested definitions.
+inline void SerializeObject(const EmptyVariant&, MessageWriter*, void*&);
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>&, MessageWriter*, void*&);
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline void SerializeObject(const T&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>&, MessageWriter*, void*&);
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>&, MessageWriter*, void*&);
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>&, MessageWriter*, void*&);
+inline void SerializeObject(const std::string&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>&, MessageWriter*, void*&);
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline void SerializeObject(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&, MessageWriter*, void*&);
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>&, MessageWriter*, void*&);
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>&, MessageWriter*, void*&);
+
+// Overload for empty variant type.
+inline void SerializeObject(const EmptyVariant& empty,
+                            MessageWriter* /*writer*/, void*& buffer) {
+  const EncodingType encoding = EncodeType(empty);
+  SerializeEncoding(encoding, buffer);
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>& variant,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeType(variant, buffer);
+  SerializeObject(variant.index(), writer, buffer);
+  return variant.Visit([writer, &buffer](const auto& value) {
+    return SerializeObject(value, writer, buffer);
+  });
+}
+
+// Overload for serializable structure/class types.
+template <typename T, typename Enabled>
+inline void SerializeObject(const T& value, MessageWriter* writer,
+                            void*& buffer) {
+  SerializableTraits<T>::SerializeObject(value, writer, buffer);
+}
+
+// Serializes the payload of a PointerWrapper.
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>& pointer,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeObject(pointer.Dereference(), writer, buffer);
+}
+
+// Serializes the payload of file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>& fd, MessageWriter* writer,
+                            void*& buffer) {
+  SerializeType(fd, buffer);
+  const Status<FileReference> status =
+      writer->GetOutputResourceMapper()->PushFileHandle(fd);
+  FileReference value = status ? status.get() : -status.error();
+  SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>& handle,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeType(handle, buffer);
+  const Status<ChannelReference> status =
+      writer->GetOutputResourceMapper()->PushChannelHandle(handle);
+  ChannelReference value = status ? status.get() : -status.error();
+  SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of BufferWrapper types.
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>& b,
+                            MessageWriter* /*writer*/, void*& buffer) {
+  const auto value_type_size =
+      sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+  SerializeType(b, buffer);
+  WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>& b,
+                            MessageWriter* /*writer*/, void*& buffer) {
+  const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+  SerializeType(b, buffer);
+  WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+
+// Serializes the payload of string types.
+template <typename StringType>
+inline void SerializeString(const StringType& s, void*& buffer) {
+  const auto value_type_size = sizeof(typename StringType::value_type);
+  SerializeType(s, buffer);
+  WriteRawData(buffer, s.data(), s.length() * value_type_size);
+}
+
+// Overload of SerializeObject() for std::string and StringWrapper. These types
+// are interchangeable and must serialize to the same format.
+inline void SerializeObject(const std::string& s, MessageWriter* /*writer*/,
+                            void*& buffer) {
+  SerializeString(s, buffer);
+}
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>& s,
+                            MessageWriter* /*writer*/, void*& buffer) {
+  SerializeString(s, buffer);
+}
+
+// Serializes the payload of array types.
+template <typename ArrayType>
+inline void SerializeArray(const ArrayType& v, MessageWriter* writer,
+                           void*& buffer) {
+  SerializeType(v, buffer);
+  for (const auto& element : v)
+    SerializeObject(element, writer, buffer);
+}
+
+// Serializes the payload for map types.
+template <typename MapType>
+inline void SerializeMap(const MapType& v, MessageWriter* writer,
+                         void*& buffer) {
+  SerializeType(v, buffer);
+  for (const auto& element : v) {
+    SerializeObject(element.first, writer, buffer);
+    SerializeObject(element.second, writer, buffer);
+  }
+}
+
+// Overload of SerializeObject() for std::vector and ArrayWrapper types. These
+// types are interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>& v,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeArray(v, writer, buffer);
+}
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>& v, MessageWriter* writer,
+                            void*& buffer) {
+  SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::array types. These types serialize to
+// the same format at std::vector and ArrayWrapper and are interchangeable in
+// certain situations.
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>& v, MessageWriter* writer,
+                            void*& buffer) {
+  SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>& v,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline void SerializeObject(
+    const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v,
+    MessageWriter* writer, void*& buffer) {
+  SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std:pair types.
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>& pair, MessageWriter* writer,
+                            void*& buffer) {
+  SerializeType(pair, buffer);
+  SerializeObject(pair.first, writer, buffer);
+  SerializeObject(pair.second, writer, buffer);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline void SerializeTuple(const std::tuple<T...>&, MessageWriter*, void*&,
+                           Index<0>) {}
+
+// Serializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline void SerializeTuple(const std::tuple<T...>& tuple, MessageWriter* writer,
+                           void*& buffer, Index<index>) {
+  SerializeTuple(tuple, writer, buffer, Index<index - 1>());
+  SerializeObject(std::get<index - 1>(tuple), writer, buffer);
+}
+
+// Overload of SerializeObject() for tuple types.
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>& tuple,
+                            MessageWriter* writer, void*& buffer) {
+  SerializeType(tuple, buffer);
+  SerializeTuple(tuple, writer, buffer, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member pointer is reached.
+template <typename Members, typename T>
+inline void SerializeMember(const T&, MessageWriter*, void*&, Index<0>) {}
+
+// Serializes each member pointer recursively.
+template <typename Members, typename T, std::size_t index>
+inline void SerializeMember(const T& object, MessageWriter* writer,
+                            void*& buffer, Index<index>) {
+  SerializeMember<Members>(object, writer, buffer, Index<index - 1>());
+  SerializeObject(Members::template At<index - 1>::Resolve(object), writer,
+                  buffer);
+}
+
+// Serializes the members of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline void SerializeMembers(const T& object, MessageWriter* writer,
+                             void*& buffer) {
+  SerializeMember<Members>(object, writer, buffer,
+                           Index<Members::MemberCount>());
+}
+
+// Top level serialization function that replaces the buffer's contents.
+template <typename T>
+inline void Serialize(const T& object, MessageWriter* writer) {
+  PDX_TRACE_NAME("Serialize");
+  const std::size_t size = GetSerializedSize(object);
+
+  // Reserve the space needed for the object(s).
+  void* buffer = writer->GetNextWriteBufferSection(size);
+  SerializeObject(object, writer, buffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Deserialization //
+///////////////////////////////////////////////////////////////////////////////
+
+inline ErrorType ReadRawDataFromNextSection(void* dest, MessageReader* reader,
+                                            const void*& start,
+                                            const void*& end, size_t size) {
+  while (AdvancePointer(start, size) > end) {
+    auto remaining_size = PointerDistance(end, start);
+    if (remaining_size > 0) {
+      memcpy(dest, start, remaining_size);
+      dest = AdvancePointer(dest, remaining_size);
+      size -= remaining_size;
+    }
+    reader->ConsumeReadBufferSectionData(AdvancePointer(start, remaining_size));
+    std::tie(start, end) = reader->GetNextReadBufferSection();
+    if (start == end)
+      return ErrorCode::INSUFFICIENT_BUFFER;
+  }
+  memcpy(dest, start, size);
+  start = AdvancePointer(start, size);
+  return ErrorCode::NO_ERROR;
+}
+
+inline ErrorType ReadRawData(void* dest, MessageReader* /*reader*/,
+                             const void*& start, const void*& end,
+                             size_t size) {
+  if (PDX_UNLIKELY(AdvancePointer(start, size) > end)) {
+    // TODO(avakulenko): Enabling reading from next sections of input buffer
+    // (using ReadRawDataFromNextSection) screws up clang compiler optimizations
+    // (probably inefficient inlining) making the whole deserialization
+    // code path about twice as slow. Investigate and enable more generic
+    // deserialization code, but right now we don't really need/support this
+    // scenario, so I keep this commented out for the time being...
+
+    // return ReadRawDataFromNextSection(dest, reader, start, end, size);
+    return ErrorCode::INSUFFICIENT_BUFFER;
+  }
+  memcpy(dest, start, size);
+  start = AdvancePointer(start, size);
+  return ErrorCode::NO_ERROR;
+}
+
+// Deserializes a primitive object from raw bytes.
+template <typename T,
+          typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline ErrorType DeserializeRaw(T* value, MessageReader* reader,
+                                const void*& start, const void*& end) {
+  return ReadRawData(value, reader, start, end, sizeof(T));
+}
+
+// Utility to deserialize POD types when the serialized type is different
+// (smaller) than the target real type. This happens when values are serialized
+// into more compact encodings.
+template <typename SerializedType, typename RealType>
+ErrorType DeserializeValue(RealType* real_value, MessageReader* reader,
+                           const void*& start, const void*& end) {
+  SerializedType serialized_value;
+  if (const auto error =
+          DeserializeRaw(&serialized_value, reader, start, end)) {
+    return error;
+  } else {
+    *real_value = serialized_value;
+    return ErrorCode::NO_ERROR;
+  }
+}
+
+inline ErrorType DeserializeEncoding(EncodingType* encoding,
+                                     MessageReader* reader, const void*& start,
+                                     const void*& end) {
+  return DeserializeRaw(encoding, reader, start, end);
+}
+
+// Overload to deserialize bool type.
+inline ErrorType DeserializeObject(bool* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsBoolEncoding(encoding)) {
+    *value = (encoding == ENCODING_TYPE_TRUE);
+    return ErrorCode::NO_ERROR;
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BOOL,
+                     encoding);
+  }
+}
+
+// Specializations to deserialize float and double types.
+inline ErrorType DeserializeObject(float* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFloat32Encoding(encoding)) {
+    return DeserializeValue<float>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(double* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFloat32Encoding(encoding)) {
+    return DeserializeValue<float>(value, reader, start, end);
+  } else if (IsFloat64Encoding(encoding)) {
+    return DeserializeValue<double>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+                     encoding);
+  }
+}
+
+// Specializations to deserialize standard integer types.
+inline ErrorType DeserializeObject(char* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsUnsignedFixintEncoding(encoding)) {
+    *value = static_cast<char>(encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (IsUInt8Encoding(encoding)) {
+    return DeserializeValue<char>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::int8_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixintEncoding(encoding)) {
+    *value = static_cast<std::int8_t>(encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (IsInt8Encoding(encoding)) {
+    return DeserializeValue<std::int8_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::uint8_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsUnsignedFixintEncoding(encoding)) {
+    *value = encoding;
+    return ErrorCode::NO_ERROR;
+  } else if (IsUInt8Encoding(encoding)) {
+    return DeserializeValue<std::uint8_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::int16_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixintEncoding(encoding)) {
+    *value = static_cast<std::int8_t>(encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (IsInt8Encoding(encoding)) {
+    return DeserializeValue<std::int8_t>(value, reader, start, end);
+  } else if (IsInt16Encoding(encoding)) {
+    return DeserializeValue<std::int16_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::uint16_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsUnsignedFixintEncoding(encoding)) {
+    *value = encoding;
+    return ErrorCode::NO_ERROR;
+  } else if (IsUInt8Encoding(encoding)) {
+    return DeserializeValue<std::uint8_t>(value, reader, start, end);
+  } else if (IsUInt16Encoding(encoding)) {
+    return DeserializeValue<std::uint16_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::int32_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixintEncoding(encoding)) {
+    *value = static_cast<std::int8_t>(encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (IsInt8Encoding(encoding)) {
+    return DeserializeValue<std::int8_t>(value, reader, start, end);
+  } else if (IsInt16Encoding(encoding)) {
+    return DeserializeValue<std::int16_t>(value, reader, start, end);
+  } else if (IsInt32Encoding(encoding)) {
+    return DeserializeValue<std::int32_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::uint32_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsUnsignedFixintEncoding(encoding)) {
+    *value = encoding;
+    return ErrorCode::NO_ERROR;
+  } else if (IsUInt8Encoding(encoding)) {
+    return DeserializeValue<std::uint8_t>(value, reader, start, end);
+  } else if (IsUInt16Encoding(encoding)) {
+    return DeserializeValue<std::uint16_t>(value, reader, start, end);
+  } else if (IsUInt32Encoding(encoding)) {
+    return DeserializeValue<std::uint32_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::int64_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixintEncoding(encoding)) {
+    *value = static_cast<std::int8_t>(encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (IsInt8Encoding(encoding)) {
+    return DeserializeValue<std::int8_t>(value, reader, start, end);
+  } else if (IsInt16Encoding(encoding)) {
+    return DeserializeValue<std::int16_t>(value, reader, start, end);
+  } else if (IsInt32Encoding(encoding)) {
+    return DeserializeValue<std::int32_t>(value, reader, start, end);
+  } else if (IsInt64Encoding(encoding)) {
+    return DeserializeValue<std::int64_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(std::uint64_t* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (IsUnsignedFixintEncoding(encoding)) {
+    *value = encoding;
+    return ErrorCode::NO_ERROR;
+  } else if (IsUInt8Encoding(encoding)) {
+    return DeserializeValue<std::uint8_t>(value, reader, start, end);
+  } else if (IsUInt16Encoding(encoding)) {
+    return DeserializeValue<std::uint16_t>(value, reader, start, end);
+  } else if (IsUInt32Encoding(encoding)) {
+    return DeserializeValue<std::uint32_t>(value, reader, start, end);
+  } else if (IsUInt64Encoding(encoding)) {
+    return DeserializeValue<std::uint64_t>(value, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+                     encoding);
+  }
+}
+
+template <typename T>
+inline EnableIfEnum<T, ErrorType> DeserializeObject(T* value,
+                                                    MessageReader* reader,
+                                                    const void*& start,
+                                                    const void*& end) {
+  std::underlying_type_t<T> enum_value;
+  ErrorType error = DeserializeObject(&enum_value, reader, start, end);
+  if (!error)
+    *value = static_cast<T>(enum_value);
+  return error;
+}
+
+// Forward declarations for nested definitions.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline ErrorType DeserializeObject(T*, MessageReader*, const void*&,
+                                   const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>*, MessageReader*,
+                                   const void*&, const void*&);
+inline ErrorType DeserializeObject(LocalHandle*, MessageReader*, const void*&,
+                                   const void*&);
+inline ErrorType DeserializeObject(LocalChannelHandle*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(BufferWrapper<std::vector<T, Allocator>>*,
+                                   MessageReader*, const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>*, MessageReader*,
+                                   const void*&, const void*&);
+inline ErrorType DeserializeObject(std::string*, MessageReader*, const void*&,
+                                   const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>*,
+                                   MessageReader*, const void*&, const void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline ErrorType DeserializeObject(
+    std::unordered_map<Key, T, Hash, KeyEqual, Allocator>*, MessageReader*,
+    const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+                                   const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+                                   const void*&, const void*&);
+inline ErrorType DeserializeObject(EmptyVariant*,
+                                   MessageReader*, const void*&,
+                                   const void*&);
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>*,
+                                   MessageReader*, const void*&,
+                                   const void*&);
+
+// Deserializes a Serializable type.
+template <typename T, typename Enable>
+inline ErrorType DeserializeObject(T* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  return SerializableTraits<T>::DeserializeObject(value, reader, start, end);
+}
+
+// Deserializes a PointerWrapper.
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>* pointer,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  return DeserializeObject(&pointer->Dereference(), reader, start, end);
+}
+
+// Deserializes the type code and size for extension types.
+inline ErrorType DeserializeExtType(EncodingType* encoding,
+                                    EncodingExtType* type, std::size_t* size,
+                                    MessageReader* reader, const void*& start,
+                                    const void*& end) {
+  if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixextEncoding(*encoding)) {
+    *size = GetFixextSize(*encoding);
+  } else if (*encoding == ENCODING_TYPE_EXT8) {
+    if (const auto error =
+            DeserializeValue<std::uint8_t>(size, reader, start, end))
+      return error;
+  } else if (*encoding == ENCODING_TYPE_EXT16) {
+    if (const auto error =
+            DeserializeValue<std::uint16_t>(size, reader, start, end))
+      return error;
+  } else if (*encoding == ENCODING_TYPE_EXT32) {
+    if (const auto error =
+            DeserializeValue<std::uint32_t>(size, reader, start, end))
+      return error;
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+                     *encoding);
+  }
+
+  // The extension type code follows the encoding and size.
+  return DeserializeRaw(type, reader, start, end);
+}
+
+// Deserializes a file handle and performs handle space translation, if
+// required.
+inline ErrorType DeserializeObject(LocalHandle* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  EncodingExtType type;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+    return error;
+  } else if (size != 2) {
+    return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+                     encoding);
+  } else if (type == ENCODING_EXT_TYPE_FILE_DESCRIPTOR) {
+    // Read the encoded file descriptor value.
+    FileReference ref;
+    if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+      return error;
+    }
+
+    return reader->GetInputResourceMapper()->GetFileHandle(ref, value)
+               ? ErrorCode::NO_ERROR
+               : ErrorCode::GET_FILE_DESCRIPTOR_FAILED;
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+                     encoding);
+  }
+}
+
+inline ErrorType DeserializeObject(LocalChannelHandle* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  EncodingExtType type;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+    return error;
+  } else if (size != 4) {
+    return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+                     encoding);
+  } else if (type == ENCODING_EXT_TYPE_CHANNEL_HANDLE) {
+    // Read the encoded channel handle value.
+    ChannelReference ref;
+    if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+      return error;
+    }
+    return reader->GetInputResourceMapper()->GetChannelHandle(ref, value)
+               ? ErrorCode::NO_ERROR
+               : ErrorCode::GET_CHANNEL_HANDLE_FAILED;
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+                     encoding);
+  }
+}
+
+// Deserializes the type code and size for bin types.
+inline ErrorType DeserializeBinType(EncodingType* encoding, std::size_t* size,
+                                    MessageReader* reader, const void*& start,
+                                    const void*& end) {
+  if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+    return error;
+  } else if (*encoding == ENCODING_TYPE_BIN8) {
+    return DeserializeValue<std::uint8_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_BIN16) {
+    return DeserializeValue<std::uint16_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_BIN32) {
+    return DeserializeValue<std::uint32_t>(size, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BINARY,
+                     *encoding);
+  }
+}
+
+// Overload of DeserializeObject() for BufferWrapper types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(
+    BufferWrapper<std::vector<T, Allocator>>* value, MessageReader* reader,
+    const void*& start, const void*& end) {
+  const auto value_type_size =
+      sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeBinType(&encoding, &size, reader, start, end))
+    return error;
+
+  // Try to resize the BufferWrapper to the size of the payload.
+  value->resize(size / value_type_size);
+
+  if (size > value->size() * value_type_size || size % value_type_size != 0) {
+    return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+  } else if (size == 0U) {
+    return ErrorCode::NO_ERROR;
+  } else {
+    return ReadRawData(value->data(), reader, start, end, size);
+  }
+}
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeBinType(&encoding, &size, reader, start, end))
+    return error;
+
+  // Try to resize the BufferWrapper to the size of the payload.
+  value->resize(size / value_type_size);
+
+  if (size > value->size() * value_type_size || size % value_type_size != 0) {
+    return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+  } else if (size == 0U) {
+    return ErrorCode::NO_ERROR;
+  } else {
+    return ReadRawData(value->data(), reader, start, end, size);
+  }
+}
+
+// Deserializes the type code and size for string types.
+inline ErrorType DeserializeStringType(EncodingType* encoding,
+                                       std::size_t* size, MessageReader* reader,
+                                       const void*& start, const void*& end) {
+  if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixstrEncoding(*encoding)) {
+    *size = GetFixstrSize(*encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (*encoding == ENCODING_TYPE_STR8) {
+    return DeserializeValue<std::uint8_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_STR16) {
+    return DeserializeValue<std::uint16_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_STR32) {
+    return DeserializeValue<std::uint32_t>(size, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_STRING,
+                     *encoding);
+  }
+}
+
+// Overload of DeserializeObject() for std::string types.
+inline ErrorType DeserializeObject(std::string* value, MessageReader* reader,
+                                   const void*& start, const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeStringType(&encoding, &size, reader, start, end)) {
+    return error;
+  } else if (size == 0U) {
+    value->clear();
+    return ErrorCode::NO_ERROR;
+  } else {
+    value->resize(size);
+    return ReadRawData(&(*value)[0], reader, start, end, size);
+  }
+}
+
+// Overload of DeserializeObject() for StringWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  const auto value_type_size = sizeof(typename StringWrapper<T>::value_type);
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeStringType(&encoding, &size, reader, start, end))
+    return error;
+
+  // Try to resize the StringWrapper to the size of the payload
+  // string.
+  value->resize(size / value_type_size);
+
+  if (size > value->length() * value_type_size || size % value_type_size != 0) {
+    return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+  } else if (size == 0U) {
+    return ErrorCode::NO_ERROR;
+  } else {
+    return ReadRawData(value->data(), reader, start, end, size);
+  }
+}
+
+// Deserializes the type code and size of array types.
+inline ErrorType DeserializeArrayType(EncodingType* encoding, std::size_t* size,
+                                      MessageReader* reader, const void*& start,
+                                      const void*& end) {
+  if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixarrayEncoding(*encoding)) {
+    *size = GetFixarraySize(*encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (*encoding == ENCODING_TYPE_ARRAY16) {
+    return DeserializeValue<std::uint16_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_ARRAY32) {
+    return DeserializeValue<std::uint32_t>(size, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_ARRAY,
+                     *encoding);
+  }
+}
+
+// Deserializes the type code and size of map types.
+inline ErrorType DeserializeMapType(EncodingType* encoding, std::size_t* size,
+                                    MessageReader* reader, const void*& start,
+                                    const void*& end) {
+  if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+    return error;
+  } else if (IsFixmapEncoding(*encoding)) {
+    *size = GetFixmapSize(*encoding);
+    return ErrorCode::NO_ERROR;
+  } else if (*encoding == ENCODING_TYPE_MAP16) {
+    return DeserializeValue<std::uint16_t>(size, reader, start, end);
+  } else if (*encoding == ENCODING_TYPE_MAP32) {
+    return DeserializeValue<std::uint32_t>(size, reader, start, end);
+  } else {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+                     *encoding);
+  }
+}
+
+// Overload for std::vector types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeArrayType(&encoding, &size, reader, start, end))
+    return error;
+
+  std::vector<T, Allocator> result(size);
+  for (std::size_t i = 0; i < size; i++) {
+    if (const auto error = DeserializeObject(&result[i], reader, start, end))
+      return error;
+  }
+
+  *value = std::move(result);
+  return ErrorCode::NO_ERROR;
+
+// TODO(eieio): Consider the benefits and trade offs of this alternative.
+#if 0
+  value->resize(size);
+  for (std::size_t i = 0; i < size; i++) {
+    if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+      return error;
+  }
+  return ErrorCode::NO_ERROR;
+#endif
+}
+
+// Deserializes an EmptyVariant value.
+inline ErrorType DeserializeObject(EmptyVariant* /*empty*/,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+
+  if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+    return error;
+  } else if (encoding != ENCODING_TYPE_NIL) {
+    return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+                     encoding);
+  } else {
+    return ErrorCode::NO_ERROR;
+  }
+}
+
+// Deserializes a Variant type.
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>* variant,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeMapType(&encoding, &size, reader, start, end)) {
+    return error;
+  }
+
+  if (size != 1)
+    return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_MAP,
+                     encoding);
+
+  std::int32_t type;
+  if (const auto error = DeserializeObject(&type, reader, start, end)) {
+    return error;
+  } else if (type < Variant<Types...>::kEmptyIndex ||
+             type >= static_cast<std::int32_t>(sizeof...(Types))) {
+    return ErrorCode::INVALID_VARIANT_ELEMENT;
+  } else {
+    variant->Become(type);
+    return variant->Visit([reader, &start, &end](auto&& value) {
+      return DeserializeObject(&value, reader, start, end);
+    });
+  }
+}
+
+// Deserializes map types.
+template <typename MapType>
+inline ErrorType DeserializeMap(MapType* value, MessageReader* reader,
+                                const void*& start, const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeMapType(&encoding, &size, reader, start, end))
+    return error;
+
+  MapType result;
+  for (std::size_t i = 0; i < size; i++) {
+    std::pair<typename MapType::key_type, typename MapType::mapped_type>
+        element;
+    if (const auto error =
+            DeserializeObject(&element.first, reader, start, end))
+      return error;
+    if (const auto error =
+            DeserializeObject(&element.second, reader, start, end))
+      return error;
+    result.emplace(std::move(element));
+  }
+
+  *value = std::move(result);
+  return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+          typename Allocator>
+inline ErrorType DeserializeObject(
+    std::unordered_map<Key, T, Hash, KeyEqual, Allocator>* value,
+    MessageReader* reader, const void*& start, const void*& end) {
+  return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeArrayType(&encoding, &size, reader, start, end)) {
+    return error;
+  }
+
+  // Try to resize the wrapper.
+  value->resize(size);
+
+  // Make sure there is enough space in the ArrayWrapper for the
+  // payload.
+  if (size > value->capacity())
+    return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+  for (std::size_t i = 0; i < size; i++) {
+    if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+      return error;
+  }
+
+  return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeArrayType(&encoding, &size, reader, start, end)) {
+    return error;
+  }
+
+  if (size != Size)
+    return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+  for (std::size_t i = 0; i < size; i++) {
+    if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+      return error;
+  }
+
+  return ErrorCode::NO_ERROR;
+}
+
+// Deserializes std::pair types.
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeArrayType(&encoding, &size, reader, start, end)) {
+    return error;
+  } else if (size != 2) {
+    return ErrorCode::UNEXPECTED_TYPE_SIZE;
+  } else if (const auto error =
+                 DeserializeObject(&value->first, reader, start, end)) {
+    return error;
+  } else if (const auto error =
+                 DeserializeObject(&value->second, reader, start, end)) {
+    return error;
+  } else {
+    return ErrorCode::NO_ERROR;
+  }
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline ErrorType DeserializeTuple(std::tuple<T...>*, MessageReader*,
+                                  const void*&, const void*, Index<0>) {
+  return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline ErrorType DeserializeTuple(std::tuple<T...>* tuple,
+                                  MessageReader* reader, const void*& start,
+                                  const void*& end, Index<index>) {
+  if (const auto error =
+          DeserializeTuple(tuple, reader, start, end, Index<index - 1>()))
+    return error;
+  else
+    return DeserializeObject(&std::get<index - 1>(*tuple), reader, start, end);
+}
+
+// Overload for standard tuple types.
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>* value,
+                                   MessageReader* reader, const void*& start,
+                                   const void*& end) {
+  EncodingType encoding;
+  std::size_t size;
+
+  if (const auto error =
+          DeserializeArrayType(&encoding, &size, reader, start, end)) {
+    return error;
+  } else if (size != sizeof...(T)) {
+    return ErrorCode::UNEXPECTED_TYPE_SIZE;
+  } else {
+    return DeserializeTuple(value, reader, start, end, Index<sizeof...(T)>());
+  }
+}
+
+// Stops template recursion when the last member of a Serializable type is
+// reached.
+template <typename Members, typename T>
+inline ErrorType DeserializeMember(T*, MessageReader*, const void*&,
+                                   const void*, Index<0>) {
+  return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline ErrorType DeserializeMember(T* value, MessageReader* reader,
+                                   const void*& start, const void*& end,
+                                   Index<index>) {
+  if (const auto error = DeserializeMember<Members>(value, reader, start, end,
+                                                    Index<index - 1>()))
+    return error;
+  else
+    return DeserializeObject(&Members::template At<index - 1>::Resolve(*value),
+                             reader, start, end);
+}
+
+// Deserializes the members of a Serializable type using the given
+// SerializableMembersType type.
+template <typename Members, typename T>
+inline ErrorType DeserializeMembers(T* value, MessageReader* reader,
+                                    const void*& start, const void*& end) {
+  return DeserializeMember<Members>(value, reader, start, end,
+                                    Index<Members::MemberCount>());
+}
+
+// Top level deserialization function.
+template <typename T>
+inline ErrorType Deserialize(T* value, MessageReader* reader) {
+  PDX_TRACE_NAME("Deserialize");
+  MessageReader::BufferSection section = reader->GetNextReadBufferSection();
+  if (section.first == section.second)
+    return ErrorCode::INSUFFICIENT_BUFFER;
+  ErrorType error =
+      DeserializeObject(value, reader, section.first, section.second);
+  reader->ConsumeReadBufferSectionData(section.first);
+  return error;
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_SERIALIZATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
new file mode 100644
index 0000000..19fc4c1
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
@@ -0,0 +1,129 @@
+#ifndef ANDROID_PDX_RPC_STRING_WRAPPER_H_
+#define ANDROID_PDX_RPC_STRING_WRAPPER_H_
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C string buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::basic_string, and may be substituted for std::basic_string
+// during serialization and deserialization. This substitution makes handling of
+// C strings more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::basic_string arguments or return values.
+template <typename CharT = std::string::value_type,
+          typename Traits = std::char_traits<CharT>>
+class StringWrapper {
+ public:
+  // Define types in the style of STL strings to support STL operators.
+  typedef Traits traits_type;
+  typedef typename Traits::char_type value_type;
+  typedef std::size_t size_type;
+  typedef value_type& reference;
+  typedef const value_type& const_reference;
+  typedef value_type* pointer;
+  typedef const value_type* const_pointer;
+
+  StringWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+  StringWrapper(pointer buffer, size_type capacity, size_type size)
+      : buffer_(&buffer[0]),
+        capacity_(capacity),
+        end_(capacity < size ? capacity : size) {}
+
+  StringWrapper(pointer buffer, size_type size)
+      : StringWrapper(buffer, size, size) {}
+
+  explicit StringWrapper(pointer buffer)
+      : StringWrapper(buffer, std::strlen(buffer)) {}
+
+  StringWrapper(const StringWrapper& other) { *this = other; }
+
+  StringWrapper(StringWrapper&& other) { *this = std::move(other); }
+
+  StringWrapper& operator=(const StringWrapper& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+    }
+
+    return *this;
+  }
+
+  StringWrapper& operator=(StringWrapper&& other) {
+    if (&other == this) {
+      return *this;
+    } else {
+      buffer_ = other.buffer_;
+      capacity_ = other.capacity_;
+      end_ = other.end_;
+      other.buffer_ = nullptr;
+      other.capacity_ = 0;
+      other.end_ = 0;
+    }
+
+    return *this;
+  }
+
+  pointer data() { return buffer_; }
+  const_pointer data() const { return buffer_; }
+
+  pointer begin() { return &buffer_[0]; }
+  pointer end() { return &buffer_[end_]; }
+  const_pointer begin() const { return &buffer_[0]; }
+  const_pointer end() const { return &buffer_[end_]; }
+
+  size_type size() const { return end_; }
+  size_type length() const { return end_; }
+  size_type max_size() const { return capacity_; }
+  size_type capacity() const { return capacity_; }
+
+  void resize(size_type size) {
+    if (size <= capacity_)
+      end_ = size;
+    else
+      end_ = capacity_;
+  }
+
+  reference operator[](size_type pos) { return buffer_[pos]; }
+  const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+  pointer buffer_;
+  size_type capacity_;
+  size_type end_;
+};
+
+// Utility functions that infer the underlying type of the string, simplifying
+// the wrapper interface.
+
+// TODO(eieio): Wrapping std::basic_string is here for completeness, but is it
+// useful?
+template <typename T, typename... Any>
+StringWrapper<const T> WrapString(const std::basic_string<T, Any...>& s) {
+  return StringWrapper<const T>(s.c_str(), s.length());
+}
+
+template <typename T, typename SizeType = std::size_t>
+StringWrapper<T> WrapString(T* s, SizeType size) {
+  return StringWrapper<T>(s, size);
+}
+
+template <typename T>
+StringWrapper<T> WrapString(T* s) {
+  return StringWrapper<T>(s);
+}
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_STRING_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
new file mode 100644
index 0000000..e5ef2aa
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to distinguish between different thread local entries or
+// "slots" in the thread local variable table. Each slot is uniquely identified
+// by (T,Index) and is independent of any other slot.
+template <typename T, std::size_t Index>
+struct ThreadLocalSlot;
+
+// Utility class to specify thread local slots using only a type.
+template <typename T>
+struct ThreadLocalTypeSlot;
+
+// Utility class to specify thread local slots using only an index.
+template <std::size_t Index>
+struct ThreadLocalIndexSlot;
+
+// Initial capacity of thread local buffer, unless otherwise specified.
+constexpr std::size_t InitialBufferCapacity = 4096;
+
+// Thread local slots for buffers used by this library to send, receive, and
+// reply to messages.
+using SendBuffer = ThreadLocalIndexSlot<0>;
+using ReceiveBuffer = ThreadLocalIndexSlot<1>;
+using ReplyBuffer = ThreadLocalIndexSlot<2>;
+
+// Provides a simple interface to thread local buffers for large IPC messages.
+// Slot provides multiple thread local slots for a given T, Allocator, Capacity
+// combination.
+template <typename T, typename Allocator = DefaultInitializationAllocator<T>,
+          std::size_t Capacity = InitialBufferCapacity,
+          typename Slot = ThreadLocalSlot<void, 0>>
+class ThreadLocalBuffer {
+ public:
+  using BufferType = std::vector<T, Allocator>;
+  using ValueType = T;
+
+  // Reserves |capacity| number of elements of capacity in the underlying
+  // buffer. Call this during startup to avoid allocation during use.
+  static void Reserve(std::size_t capacity) {
+    PDX_TRACE_NAME("ThreadLocalBuffer::Reserve");
+    InitializeBuffer(capacity);
+    buffer_->reserve(capacity);
+  }
+
+  // Resizes the buffer to |size| elements.
+  static void Resize(std::size_t size) {
+    PDX_TRACE_NAME("ThreadLocalBuffer::Resize");
+    InitializeBuffer(size);
+    buffer_->resize(size);
+  }
+
+  // Gets a reference to the underlying buffer after reserving |capacity|
+  // elements. The current size of the buffer is left intact. The returned
+  // reference is valid until FreeBuffer() is called.
+  static BufferType& GetBuffer(std::size_t capacity = Capacity) {
+    PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer");
+    Reserve(capacity);
+    return *buffer_;
+  }
+
+  // Gets a reference to the underlying buffer after reserving |Capacity|
+  // elements. The current size of the buffer is set to zero. The returned
+  // reference is valid until FreeBuffer() is called.
+  static BufferType& GetEmptyBuffer() {
+    PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer");
+    Reserve(Capacity);
+    buffer_->clear();
+    return *buffer_;
+  }
+
+  // Gets a reference to the underlying buffer after resizing it to |size|
+  // elements. The returned reference is valid until FreeBuffer() is called.
+  static BufferType& GetSizedBuffer(std::size_t size = Capacity) {
+    PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer");
+    Resize(size);
+    return *buffer_;
+  }
+
+  // Frees the underlying buffer. The buffer will be reallocated if any of the
+  // methods above are called.
+  static void FreeBuffer() {
+    if (buffer_) {
+      GetBufferGuard().reset(buffer_ = nullptr);
+    }
+  }
+
+ private:
+  friend class ThreadLocalBufferTest;
+
+  static void InitializeBuffer(std::size_t capacity) {
+    if (!buffer_) {
+      GetBufferGuard().reset(buffer_ = new BufferType(capacity));
+    }
+  }
+
+  // Work around performance issues with thread-local dynamic initialization
+  // semantics by using a normal pointer in parallel with a std::unique_ptr. The
+  // std::unique_ptr is never dereferenced, only assigned, to avoid the high
+  // cost of dynamic initialization checks, while still providing automatic
+  // cleanup. The normal pointer provides fast access to the buffer object.
+  // Never dereference buffer_guard or performance could be severely impacted
+  // by slow implementations of TLS dynamic initialization.
+  static thread_local BufferType* buffer_;
+
+  static std::unique_ptr<BufferType>& GetBufferGuard() {
+    PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard");
+    static thread_local std::unique_ptr<BufferType> buffer_guard;
+    return buffer_guard;
+  }
+};
+
+// Instantiation of the static ThreadLocalBuffer::buffer_ member.
+template <typename T, typename Allocator, std::size_t Capacity, typename Slot>
+thread_local
+    typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType*
+        ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_;
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/type_operators.h b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
new file mode 100644
index 0000000..811bd87
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
@@ -0,0 +1,195 @@
+#ifndef ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+#define ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+
+#include <array>
+#include <map>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/copy_cv_reference.h>
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/rpc/string_wrapper.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Simplifies type expressions.
+template <typename T>
+using Decay = typename std::decay<T>::type;
+
+// Compares the underlying type of A and B.
+template <typename A, typename B>
+using IsEquivalent = typename std::is_same<Decay<A>, Decay<B>>::type;
+
+// Logical AND over template parameter pack.
+template <typename... T>
+struct And : std::false_type {};
+template <typename A, typename B>
+struct And<A, B> : std::integral_constant<bool, A::value && B::value> {};
+template <typename A, typename B, typename... Rest>
+struct And<A, B, Rest...> : And<A, And<B, Rest...>> {};
+
+// Determines whether A is convertible to B (serializes to the same format)
+// using these rules:
+//    1. std:vector<T, Any...> is convertible to ArrayWrapper<T>.
+//    2. ArrayWrapper<T> is convertible to std:vector<T, Any...>.
+//    3. std::basic_string<T, Any...> is convertible to StringWrapper<T>.
+//    4. StringWrapper<T> is convertible to std::basic_string<T, Any...>.
+//    5. BufferWrapper<T*> is convertible to BufferWrapper<std::vector<T,
+//    Any...>>.
+//    6. BufferWrapper<std::vector<T, ...>> is convertible to BufferWrapper<T*>.
+//    7. The value type T of A and B must match.
+
+// Compares A and B for convertibility. This base type determines convertibility
+// by equivalence of the underlying types of A and B. Specializations of this
+// type handle the rules for which complex types are convertible.
+template <typename A, typename B>
+struct IsConvertible : IsEquivalent<A, B> {};
+
+// Compares TT<A, ...> and TT<B, ...>; these are convertible if A and B are
+// convertible.
+template <template <typename, typename...> class TT, typename A, typename B,
+          typename... AnyA, typename... AnyB>
+struct IsConvertible<TT<A, AnyA...>, TT<B, AnyB...>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares TT<KeyA, ValueA, ...> and TT<KeyB, ValueB, ...>; these are
+// convertible if KeyA and KeyB are
+// convertible and ValueA and ValueB are convertible.
+template <template <typename, typename, typename...> class TT, typename KeyA,
+          typename ValueA, typename KeyB, typename ValueB, typename... AnyA,
+          typename... AnyB>
+struct IsConvertible<TT<KeyA, ValueA, AnyA...>, TT<KeyB, ValueB, AnyB...>>
+    : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+          IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares two std::pairs to see if the corresponding elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::pair<C, D>>
+    : And<IsConvertible<Decay<A>, Decay<C>>,
+          IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares std::pair with a two-element std::tuple to see if the corresponding
+// elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::tuple<C, D>>
+    : And<IsConvertible<Decay<A>, Decay<C>>,
+          IsConvertible<Decay<B>, Decay<D>>> {};
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::tuple<A, B>, std::pair<C, D>>
+    : And<IsConvertible<Decay<A>, Decay<C>>,
+          IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares two std::tuples to see if the corresponding elements are
+// convertible.
+template <typename... A, typename... B>
+struct IsConvertible<std::tuple<A...>, std::tuple<B...>>
+    : And<IsConvertible<Decay<A>, Decay<B>>...> {};
+
+// Compares std::vector, std::array, and ArrayWrapper; these are convertible if
+// the value types are convertible.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::vector<A, Any...>, ArrayWrapper<B>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<ArrayWrapper<A>, std::vector<B, Any...>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::vector<A, Any...>, std::array<B, Size>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, std::vector<B, Any...>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<ArrayWrapper<A>, std::array<B, Size>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, ArrayWrapper<B>>
+    : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares std::map and std::unordered_map; these are convertible if the keys
+// are convertible and the values are convertible.
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+          typename... AnyA, typename... AnyB>
+struct IsConvertible<std::map<KeyA, ValueA, AnyA...>,
+                     std::unordered_map<KeyB, ValueB, AnyB...>>
+    : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+          IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+          typename... AnyA, typename... AnyB>
+struct IsConvertible<std::unordered_map<KeyA, ValueA, AnyA...>,
+                     std::map<KeyB, ValueB, AnyB...>>
+    : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+          IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares BufferWrapper<A*> and BufferWrapper<std::vector<B>>; these are
+// convertible if A and B are equivalent. Allocator types are not relevant to
+// convertibility.
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<A*>,
+                     BufferWrapper<std::vector<B, Allocator>>>
+    : IsEquivalent<A, B> {};
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<std::vector<A, Allocator>>,
+                     BufferWrapper<B*>> : IsEquivalent<A, B> {};
+template <typename A, typename B, typename AllocatorA, typename AllocatorB>
+struct IsConvertible<BufferWrapper<std::vector<A, AllocatorA>>,
+                     BufferWrapper<std::vector<B, AllocatorB>>>
+    : IsEquivalent<A, B> {};
+template <typename A, typename B>
+struct IsConvertible<BufferWrapper<A*>, BufferWrapper<B*>>
+    : IsEquivalent<A, B> {};
+
+// Compares std::basic_string<A, ...> and StringWrapper<B>; these are
+// convertible if A and B are equivalent.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::basic_string<A, Any...>, StringWrapper<B>>
+    : IsEquivalent<A, B> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<StringWrapper<A>, std::basic_string<B, Any...>>
+    : IsEquivalent<A, B> {};
+
+// Compares PointerWrapper<A> and B; these are convertible if A and B are
+// convertible.
+template <typename A, typename B>
+struct IsConvertible<PointerWrapper<A>, B> : IsConvertible<Decay<A>, Decay<B>> {
+};
+template <typename A, typename B>
+struct IsConvertible<A, PointerWrapper<B>> : IsConvertible<Decay<A>, Decay<B>> {
+};
+
+// LocalHandle is convertible to RemoteHandle on the service side. This means
+// that a RemoteHandle may be supplied by a service when the protocol calls for
+// a LocalHandle return value. The other way around is not safe and can leak
+// file descriptors. The ServicePayload class enforces this policy by only
+// supporting RemoteHandle for pushed handles.
+template <>
+struct IsConvertible<LocalHandle, RemoteHandle> : std::true_type {};
+template <>
+struct IsConvertible<LocalHandle, BorrowedHandle> : std::true_type {};
+
+template <>
+struct IsConvertible<LocalChannelHandle, RemoteChannelHandle> : std::true_type {
+};
+template <>
+struct IsConvertible<LocalChannelHandle, BorrowedChannelHandle>
+    : std::true_type {};
+
+// Conditionally "rewrites" type A as type B, including cv-reference qualifiers,
+// iff A is convertible to B.
+template <typename A, typename B>
+using ConditionalRewrite =
+    typename std::conditional<IsConvertible<Decay<A>, Decay<B>>::value,
+                              CopyCVReferenceType<A, B>, A>::type;
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+#endif  //  ANDROID_PDX_RPC_TYPE_OPERATORS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
new file mode 100644
index 0000000..cb44a51
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -0,0 +1,686 @@
+#ifndef ANDROID_PDX_RPC_VARIANT_H_
+#define ANDROID_PDX_RPC_VARIANT_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Type tag denoting an empty variant.
+struct EmptyVariant {};
+
+namespace detail {
+
+// Type for matching tagged overloads.
+template <typename T>
+struct TypeTag {};
+
+// Determines the type of the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>;
+
+// Determines the type tag for the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>;
+
+// Enable if T(Args...) is well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfConstructible =
+    typename std::enable_if<std::is_constructible<T, Args...>::value, R>::type;
+// Enable if T(Args...) is not well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfNotConstructible =
+    typename std::enable_if<!std::is_constructible<T, Args...>::value, R>::type;
+
+// Determines whether T is an element of Types...;
+template <typename... Types>
+struct HasType : std::false_type {};
+template <typename T, typename U>
+struct HasType<T, U> : std::is_same<std::decay_t<T>, std::decay_t<U>> {};
+template <typename T, typename First, typename... Rest>
+struct HasType<T, First, Rest...>
+    : std::integral_constant<bool, HasType<T, First>::value ||
+                                       HasType<T, Rest...>::value> {};
+
+// Defines set operations on a set of Types...
+template <typename... Types>
+struct Set {
+  // Default specialization catches the empty set, which is always a subset.
+  template <typename...>
+  struct IsSubset : std::true_type {};
+  template <typename T>
+  struct IsSubset<T> : HasType<T, Types...> {};
+  template <typename First, typename... Rest>
+  struct IsSubset<First, Rest...>
+      : std::integral_constant<bool, IsSubset<First>::value &&
+                                         IsSubset<Rest...>::value> {};
+};
+
+// Determines the number of elements of Types... that are constructible from
+// From.
+template <typename... Types>
+struct ConstructibleCount;
+template <typename From, typename To>
+struct ConstructibleCount<From, To>
+    : std::integral_constant<std::size_t,
+                             std::is_constructible<To, From>::value> {};
+template <typename From, typename First, typename... Rest>
+struct ConstructibleCount<From, First, Rest...>
+    : std::integral_constant<std::size_t,
+                             std::is_constructible<First, From>::value +
+                                 ConstructibleCount<From, Rest...>::value> {};
+
+// Enable if T is an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfElement =
+    typename std::enable_if<HasType<T, Types...>::value, R>::type;
+// Enable if T is not an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfNotElement =
+    typename std::enable_if<!HasType<T, Types...>::value, R>::type;
+
+// Enable if T is convertible to an element of Types... T is considered
+// convertible IIF a single element of Types... is assignable from T and T is
+// not a direct element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfConvertible =
+    typename std::enable_if<!HasType<T, Types...>::value &&
+                                ConstructibleCount<T, Types...>::value == 1,
+                            R>::type;
+
+// Enable if T is assignable to an element of Types... T is considered
+// assignable IFF a single element of Types... is constructible from T or T is a
+// direct element of Types.... Note that T is REQUIRED to be an element of
+// Types... when multiple elements are constructible from T to prevent ambiguity
+// in conversion.
+template <typename R, typename T, typename... Types>
+using EnableIfAssignable =
+    typename std::enable_if<HasType<T, Types...>::value ||
+                                ConstructibleCount<T, Types...>::value == 1,
+                            R>::type;
+
+// Selects a type for SFINAE constructor selection.
+template <bool CondA, typename SelectA, typename SelectB>
+using Select = std::conditional_t<CondA, SelectA, SelectB>;
+
+// Recursive union type.
+template <typename... Types>
+union Union;
+
+// Specialization handling a singular type, terminating template recursion.
+template <typename Type>
+union Union<Type> {
+  Union() {}
+  ~Union() {}
+
+  template <typename T>
+  Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value)
+      : first_(std::forward<T>(value)) {
+    *index_out = index;
+  }
+  template <typename T, typename = EnableIfAssignable<void, T, Type>>
+  Union(std::int32_t index, std::int32_t* index_out, T&& value)
+      : first_(std::forward<T>(value)) {
+    *index_out = index;
+  }
+
+  Type& get(TypeTag<Type>) { return first_; }
+  const Type& get(TypeTag<Type>) const { return first_; }
+  EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; }
+  constexpr std::int32_t index(TypeTag<Type>) const { return 0; }
+
+  template <typename... Args>
+  std::int32_t Construct(TypeTag<Type>, Args&&... args) {
+    new (&first_) Type(std::forward<Args>(args)...);
+    return 0;
+  }
+  template <typename... Args>
+  EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) {
+    new (&first_) Type(std::forward<Args>(args)...);
+    return 0;
+  }
+
+  void Destruct(std::int32_t target_index) {
+    if (target_index == index(TypeTag<Type>{})) {
+      (&get(TypeTag<Type>{}))->~Type();
+    }
+  }
+
+  template <typename T>
+  bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) {
+    if (target_index == 0) {
+      first_ = std::forward<T>(value);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  template <typename T>
+  EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index,
+                                              T&& value) {
+    if (target_index == 0) {
+      first_ = std::forward<T>(value);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  template <typename T>
+  EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/,
+                                                 T&& /*value*/) {
+    return false;
+  }
+
+  template <typename Op>
+  decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+    if (target_index == index(TypeTag<Type>{}))
+      return std::forward<Op>(op)(get(TypeTag<Type>{}));
+    else
+      return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+  }
+  template <typename Op>
+  decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+    if (target_index == index(TypeTag<Type>{}))
+      return std::forward<Op>(op)(get(TypeTag<Type>{}));
+    else
+      return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+  }
+
+  template <typename... Args>
+  bool Become(std::int32_t target_index, Args&&... args) {
+    if (target_index == index(TypeTag<Type>{})) {
+      Construct(TypeTag<Type>{}, std::forward<Args>(args)...);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+ private:
+  Type first_;
+};
+
+// Specialization that recursively unions types from the paramater pack.
+template <typename First, typename... Rest>
+union Union<First, Rest...> {
+  Union() {}
+  ~Union() {}
+
+  template <typename T>
+  Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value)
+      : first_(std::forward<T>(value)) {
+    *index_out = index;
+  }
+  template <typename T, typename U>
+  Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value)
+      : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {}
+
+  struct FirstType {};
+  struct RestType {};
+  template <typename T>
+  using SelectConstructor =
+      Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>;
+
+  template <typename T>
+  Union(std::int32_t index, std::int32_t* index_out, T&& value)
+      : Union(index, index_out, std::forward<T>(value),
+              SelectConstructor<T>{}) {}
+
+  template <typename T>
+  Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType)
+      : first_(std::forward<T>(value)) {
+    *index_out = index;
+  }
+  template <typename T>
+  Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType)
+      : rest_(index + 1, index_out, std::forward<T>(value)) {}
+
+  First& get(TypeTag<First>) { return first_; }
+  const First& get(TypeTag<First>) const { return first_; }
+  constexpr std::int32_t index(TypeTag<First>) const { return 0; }
+
+  template <typename T>
+  T& get(TypeTag<T>) {
+    return rest_.template get(TypeTag<T>{});
+  }
+  template <typename T>
+  const T& get(TypeTag<T>) const {
+    return rest_.template get(TypeTag<T>{});
+  }
+  template <typename T>
+  constexpr std::int32_t index(TypeTag<T>) const {
+    return 1 + rest_.template index(TypeTag<T>{});
+  }
+
+  template <typename... Args>
+  std::int32_t Construct(TypeTag<First>, Args&&... args) {
+    new (&first_) First(std::forward<Args>(args)...);
+    return 0;
+  }
+  template <typename T, typename... Args>
+  std::int32_t Construct(TypeTag<T>, Args&&... args) {
+    return 1 +
+           rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...);
+  }
+
+  template <typename... Args>
+  EnableIfConstructible<std::int32_t, First, Args...> Construct(
+      Args&&... args) {
+    new (&first_) First(std::forward<Args>(args)...);
+    return 0;
+  }
+  template <typename... Args>
+  EnableIfNotConstructible<std::int32_t, First, Args...> Construct(
+      Args&&... args) {
+    return 1 + rest_.template Construct(std::forward<Args>(args)...);
+  }
+
+  void Destruct(std::int32_t target_index) {
+    if (target_index == index(TypeTag<First>{})) {
+      (get(TypeTag<First>{})).~First();
+    } else {
+      rest_.Destruct(target_index - 1);
+    }
+  }
+
+  template <typename T>
+  bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) {
+    if (target_index == 0) {
+      first_ = std::forward<T>(value);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  template <typename T, typename U>
+  bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) {
+    return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value));
+  }
+  template <typename T>
+  EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index,
+                                               T&& value) {
+    if (target_index == 0) {
+      first_ = std::forward<T>(value);
+      return true;
+    } else {
+      return rest_.Assign(target_index - 1, std::forward<T>(value));
+    }
+  }
+  template <typename T>
+  EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index,
+                                                  T&& value) {
+    return rest_.Assign(target_index - 1, std::forward<T>(value));
+  }
+
+  // Recursively traverses the union and calls Op on the active value when the
+  // active type is found. If the union is empty Op is called on EmptyVariant.
+  // TODO(eieio): This could be refactored into an array or jump table. It's
+  // unclear whether this would be more efficient for practical variant arity.
+  template <typename Op>
+  decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+    if (target_index == index(TypeTag<First>{}))
+      return std::forward<Op>(op)(get(TypeTag<First>{}));
+    else
+      return rest_.Visit(target_index - 1, std::forward<Op>(op));
+  }
+  template <typename Op>
+  decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+    if (target_index == index(TypeTag<First>{}))
+      return std::forward<Op>(op)(get(TypeTag<First>{}));
+    else
+      return rest_.Visit(target_index - 1, std::forward<Op>(op));
+  }
+
+  template <typename... Args>
+  bool Become(std::int32_t target_index, Args&&... args) {
+    if (target_index == index(TypeTag<First>{})) {
+      Construct(TypeTag<First>{}, std::forward<Args>(args)...);
+      return true;
+    } else {
+      return rest_.Become(target_index - 1, std::forward<Args>(args)...);
+    }
+  }
+
+ private:
+  First first_;
+  Union<Rest...> rest_;
+};
+
+}  // namespace detail
+
+template <typename... Types>
+class Variant {
+ private:
+  // Convenience types.
+  template <typename T>
+  using TypeTag = detail::TypeTag<T>;
+  template <typename T>
+  using DecayedTypeTag = TypeTag<std::decay_t<T>>;
+  template <std::size_t I>
+  using TypeForIndex = detail::TypeForIndex<I, Types...>;
+  template <std::size_t I>
+  using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>;
+  template <typename T>
+  using HasType = detail::HasType<T, Types...>;
+  template <typename R, typename T>
+  using EnableIfElement = detail::EnableIfElement<R, T, Types...>;
+  template <typename R, typename T>
+  using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>;
+  template <typename R, typename T>
+  using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>;
+
+  struct Direct {};
+  struct Convert {};
+  template <typename T>
+  using SelectConstructor = detail::Select<HasType<T>::value, Direct, Convert>;
+
+  // Constructs by type tag when T is an direct element of Types...
+  template <typename T>
+  explicit Variant(T&& value, Direct)
+      : value_(0, &index_, DecayedTypeTag<T>{}, std::forward<T>(value)) {}
+  // Conversion constructor when T is not a direct element of Types...
+  template <typename T>
+  explicit Variant(T&& value, Convert)
+      : value_(0, &index_, std::forward<T>(value)) {}
+
+ public:
+  // Variants are default construcible, regardless of whether the elements are
+  // default constructible. Default consruction yields an empty Variant.
+  Variant() {}
+  explicit Variant(EmptyVariant) {}
+  ~Variant() { Destruct(); }
+
+  // Copy and move construction from Variant types. Each element of OtherTypes
+  // must be convertible to an element of Types.
+  template <typename... OtherTypes>
+  explicit Variant(const Variant<OtherTypes...>& other) {
+    other.Visit([this](const auto& value) { Construct(value); });
+  }
+  template <typename... OtherTypes>
+  explicit Variant(Variant<OtherTypes...>&& other) {
+    other.Visit([this](auto&& value) { Construct(std::move(value)); });
+  }
+
+  // Construction from non-Variant types.
+  template <typename T, typename = EnableIfAssignable<void, T>>
+  explicit Variant(T&& value)
+      : Variant(std::forward<T>(value), SelectConstructor<T>{}) {}
+
+  // Performs assignment from type T belonging to Types. This overload takes
+  // priority to prevent implicit conversion in cases where T is implicitly
+  // convertible to multiple elements of Types.
+  template <typename T>
+  EnableIfElement<Variant&, T> operator=(T&& value) {
+    Assign(DecayedTypeTag<T>{}, std::forward<T>(value));
+    return *this;
+  }
+
+  // Performs assignment from type T not belonging to Types. This overload
+  // matches in cases where conversion is the only viable option.
+  template <typename T>
+  EnableIfConvertible<Variant&, T> operator=(T&& value) {
+    Assign(std::forward<T>(value));
+    return *this;
+  }
+
+  // Handles assignment from the empty type. This overload supports assignment
+  // in visitors using generic lambdas.
+  Variant& operator=(EmptyVariant) {
+    Assign(EmptyVariant{});
+    return *this;
+  }
+
+  // Assignment from Variant types. Each element of OtherTypes must be
+  // convertible to an element of Types. Forwards through non-Variant assignment
+  // operators to apply conversion checks.
+  template <typename... OtherTypes>
+  Variant& operator=(const Variant<OtherTypes...>& other) {
+    other.Visit([this](const auto& value) { *this = value; });
+    return *this;
+  }
+  template <typename... OtherTypes>
+  Variant& operator=(Variant<OtherTypes...>&& other) {
+    other.Visit([this](auto&& value) { *this = std::move(value); });
+    return *this;
+  }
+
+  // Becomes the target type, constructing a new element from the given
+  // arguments if necessary. No action is taken if the active element is already
+  // the target type. Otherwise the active element is destroyed and replaced by
+  // constructing an element of the new type using |Args|. An invalid target
+  // type index results in an empty Variant.
+  template <typename... Args>
+  void Become(std::int32_t target_index, Args&&... args) {
+    if (target_index != index()) {
+      Destruct();
+      index_ = value_.Become(target_index, std::forward<Args>(args)...)
+                   ? target_index
+                   : kEmptyIndex;
+    }
+  }
+
+  // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked
+  // on EmptyVariant.
+  template <typename Op>
+  decltype(auto) Visit(Op&& op) {
+    return value_.Visit(index_, std::forward<Op>(op));
+  }
+  template <typename Op>
+  decltype(auto) Visit(Op&& op) const {
+    return value_.Visit(index_, std::forward<Op>(op));
+  }
+
+  // Index returned when the Variant is empty.
+  enum : std::int32_t { kEmptyIndex = -1 };
+
+  // Returns the index of the given type.
+  template <typename T>
+  constexpr std::int32_t index_of() const {
+    static_assert(HasType<T>::value, "T is not an element type of Variant.");
+    return value_.template index(DecayedTypeTag<T>{});
+  }
+
+  // Returns the index of the active type. If the Variant is empty -1 is
+  // returned.
+  std::int32_t index() const { return index_; }
+
+  // Returns true if the given type is active, false otherwise.
+  template <typename T>
+  bool is() const {
+    static_assert(HasType<T>::value, "T is not an element type of Variant.");
+    return index() == index_of<T>();
+  }
+
+  // Returns true if the Variant is empty, false otherwise.
+  bool empty() const { return index() == kEmptyIndex; }
+
+  // Element accessors. Returns a pointer to the active value if the given
+  // type/index is active, otherwise nullptr is returned.
+  template <typename T>
+  T* get() {
+    if (is<T>())
+      return &value_.template get(DecayedTypeTag<T>{});
+    else
+      return nullptr;
+  }
+  template <typename T>
+  const T* get() const {
+    if (is<T>())
+      return &value_.template get(DecayedTypeTag<T>{});
+    else
+      return nullptr;
+  }
+  template <std::size_t I>
+  TypeForIndex<I>* get() {
+    if (is<TypeForIndex<I>>())
+      return &value_.template get(TypeTagForIndex<I>{});
+    else
+      return nullptr;
+  }
+  template <std::size_t I>
+  const TypeForIndex<I>* get() const {
+    if (is<TypeForIndex<I>>())
+      return &value_.template get(TypeTagForIndex<I>{});
+    else
+      return nullptr;
+  }
+
+ private:
+  std::int32_t index_ = kEmptyIndex;
+  detail::Union<std::decay_t<Types>...> value_;
+
+  // Constructs an element from the given arguments and sets the Variant to the
+  // resulting type.
+  template <typename... Args>
+  void Construct(Args&&... args) {
+    index_ = value_.template Construct(std::forward<Args>(args)...);
+  }
+  void Construct(EmptyVariant) {}
+
+  // Destroys the active element of the Variant.
+  void Destruct() { value_.Destruct(index_); }
+
+  // Assigns the Variant when non-empty and the current type matches the target
+  // type, otherwise destroys the current value and constructs a element of the
+  // new type. Tagged assignment is used when T is an element of the Variant to
+  // prevent implicit conversion in cases where T is implicitly convertible to
+  // multiple element types.
+  template <typename T, typename U>
+  void Assign(TypeTag<T>, U&& value) {
+    if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) {
+      Destruct();
+      Construct(TypeTag<T>{}, std::forward<U>(value));
+    }
+  }
+  template <typename T>
+  void Assign(T&& value) {
+    if (!value_.template Assign(index_, std::forward<T>(value))) {
+      Destruct();
+      Construct(std::forward<T>(value));
+    }
+  }
+  // Handles assignment from an empty Variant.
+  void Assign(EmptyVariant) { Destruct(); }
+};
+
+// Utility type to extract/convert values from a variant. This class simplifies
+// conditional logic to get/move/swap/action values from a variant when one or
+// more elements are compatible with the destination type.
+//
+// Example:
+//    Variant<int, bool, std::string> v(10);
+//    bool bool_value;
+//    if (IfAnyOf<int, bool>::Get(v, &bool_value)) {
+//      DoSomething(bool_value);
+//    } else {
+//      HandleInvalidType();
+//    }
+//    IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); });
+//
+template <typename... ValidTypes>
+struct IfAnyOf {
+  // Calls Op on the underlying value of the variant and returns true when the
+  // variant is a valid type, otherwise does nothing and returns false.
+  template <typename Op, typename... Types>
+  static bool Call(Variant<Types...>* variant, Op&& op) {
+    static_assert(
+        detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+        "ValidTypes may only contain element types from the Variant.");
+    return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+  }
+  template <typename Op, typename... Types>
+  static bool Call(const Variant<Types...>* variant, Op&& op) {
+    static_assert(
+        detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+        "ValidTypes may only contain element types from the Variant.");
+    return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+  }
+
+  // Gets/converts the underlying value of the variant to type T and returns
+  // true when the variant is a valid type, otherwise does nothing and returns
+  // false.
+  template <typename T, typename... Types>
+  static bool Get(const Variant<Types...>* variant, T* value_out) {
+    return Call(variant,
+                [value_out](const auto& value) { *value_out = value; });
+  }
+
+  // Moves the underlying value of the variant and returns true when the variant
+  // is a valid type, otherwise does nothing and returns false.
+  template <typename T, typename... Types>
+  static bool Take(Variant<Types...>* variant, T* value_out) {
+    return Call(variant,
+                [value_out](auto&& value) { *value_out = std::move(value); });
+  }
+
+  // Swaps the underlying value of the variant with |*value_out| and returns
+  // true when the variant is a valid type, otherwise does nothing and returns
+  // false.
+  template <typename T, typename... Types>
+  static bool Swap(Variant<Types...>* variant, T* value_out) {
+    return Call(variant,
+                [value_out](auto&& value) { std::swap(*value_out, value); });
+  }
+
+ private:
+  template <typename Op>
+  struct CallOp {
+    Op&& op;
+    template <typename U>
+    detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) {
+      return false;
+    }
+    template <typename U>
+    detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) {
+      std::forward<Op>(op)(value);
+      return true;
+    }
+    template <typename U>
+    detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) {
+      std::forward<Op>(op)(std::forward<U>(value));
+      return true;
+    }
+  };
+};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+// Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant.
+namespace std {
+
+template <typename T, typename... Types>
+inline T& get(::android::pdx::rpc::Variant<Types...>& v) {
+  return *v.template get<T>();
+}
+template <typename T, typename... Types>
+inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) {
+  return std::move(*v.template get<T>());
+}
+template <typename T, typename... Types>
+inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) {
+  return *v.template get<T>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+    ::android::pdx::rpc::Variant<Types...>& v) {
+  return *v.template get<I>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get(
+    ::android::pdx::rpc::Variant<Types...>&& v) {
+  return std::move(*v.template get<I>());
+}
+template <std::size_t I, typename... Types>
+inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+    const ::android::pdx::rpc::Variant<Types...>& v) {
+  return *v.template get<I>();
+}
+
+}  // namespace std
+
+#endif  // ANDROID_PDX_RPC_VARIANT_H_
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
new file mode 100644
index 0000000..0d30614
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -0,0 +1,742 @@
+#ifndef ANDROID_PDX_SERVICE_H_
+#define ANDROID_PDX_SERVICE_H_
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "pdx/channel_handle.h"
+#include "pdx/file_handle.h"
+#include "pdx/message_reader.h"
+#include "pdx/message_writer.h"
+#include "pdx/service_endpoint.h"
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+namespace opcodes {
+
+/*
+ * Reserved message opcodes used by libpdx. The reserved opcodes start at the
+ * max positive signed integer for the system and go down.
+ * In contrast, service opcodes start at zero and go up. This scheme leaves
+ * most of the positive integer space for services, a tiny fraction of the
+ * positive integer space for the framework, and the entire negative integer
+ * space for the kernel.
+ */
+#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n))  // 0x7fff..ffff - n
+
+enum {
+  // System message sent when a new client channel is open.
+  CHANNEL_OPEN = -1,
+  // System message sent when a channel is closed.
+  CHANNEL_CLOSE = -2,
+  // Request the service to reload system properties.
+  PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0),
+  // Request the service to dump state and return it in a text buffer.
+  PDX_OPCODE(DUMP_STATE, 1),
+};
+
+}  // namespace opcodes
+
+/*
+ * Base class of service-side per-channel context classes.
+ */
+class Channel : public std::enable_shared_from_this<Channel> {
+ public:
+  Channel() {}
+  virtual ~Channel() {}
+
+  /*
+   * Utility to get a shared_ptr reference from the channel context pointer.
+   */
+  static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+};
+
+/*
+ * Message class represents an RPC message, and implicitly the blocked sender
+ * waiting for a response. Every message should get a reply, at some point
+ * (unless the endpoint is closed), to prevent clients from blocking
+ * indefinitely. In order to enforce this and prevent leaking message ids,
+ * Message automatically replies with an error to the client on destruction,
+ * unless one of two things happens:
+ *
+ *     1. The service calls one of the reply methods before the Message is
+ *        destroyed.
+ *     2. The responsibility for the message is moved to another instance of
+ *        Message, using either move construction or move assignment.
+ *
+ * The second case is useful for services that need to delay responding to a
+ * sender until a later time. In this situation the service can move the
+ * Message to another instance in a suitable data structure for later use. The
+ * moved-to Message then takes on the same behavior and responsibilities
+ * described above.
+ */
+class Message : public OutputResourceMapper, public InputResourceMapper {
+ public:
+  Message();
+  Message(const MessageInfo& info);
+  ~Message();
+
+  /*
+   * Message objects support move construction and assignment.
+   */
+  Message(Message&& other);
+  Message& operator=(Message&& other);
+
+  /*
+   * Read/write payload, in either single buffer or iovec form.
+   */
+  Status<size_t> ReadVector(const iovec* vector, size_t vector_length);
+  Status<size_t> Read(void* buffer, size_t length);
+  Status<size_t> WriteVector(const iovec* vector, size_t vector_length);
+  Status<size_t> Write(const void* buffer, size_t length);
+
+  template <size_t N>
+  inline Status<size_t> ReadVector(const iovec (&vector)[N]) {
+    return ReadVector(vector, N);
+  }
+
+  template <size_t N>
+  inline Status<size_t> WriteVector(const iovec (&vector)[N]) {
+    return WriteVector(vector, N);
+  }
+
+  // Helper functions to read/write all requested bytes, and return EIO if not
+  // all were read/written.
+  Status<void> ReadVectorAll(const iovec* vector, size_t vector_length);
+  Status<void> WriteVectorAll(const iovec* vector, size_t vector_length);
+
+  inline Status<void> ReadAll(void* buffer, size_t length) {
+    Status<size_t> status = Read(buffer, length);
+    if (status && status.get() < length)
+      status.SetError(EIO);
+    Status<void> ret;
+    ret.PropagateError(status);
+    return ret;
+  }
+  inline Status<void> WriteAll(const void* buffer, size_t length) {
+    Status<size_t> status = Write(buffer, length);
+    if (status && status.get() < length)
+      status.SetError(EIO);
+    Status<void> ret;
+    ret.PropagateError(status);
+    return ret;
+  }
+
+  template <size_t N>
+  inline Status<void> ReadVectorAll(const iovec (&vector)[N]) {
+    return ReadVectorAll(vector, N);
+  }
+
+  template <size_t N>
+  inline Status<void> WriteVectorAll(const iovec (&vector)[N]) {
+    return WriteVectorAll(vector, N);
+  }
+
+  // OutputResourceMapper
+  Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+  Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+  Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) override;
+
+  // InputResourceMapper
+  bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+  bool GetChannelHandle(ChannelReference ref,
+                        LocalChannelHandle* handle) override;
+
+  /*
+   * Various ways to reply to a message.
+   */
+  Status<void> Reply(int return_code);
+  Status<void> ReplyError(unsigned int error);
+  Status<void> ReplyFileDescriptor(unsigned int fd);
+  Status<void> Reply(const LocalHandle& handle);
+  Status<void> Reply(const BorrowedHandle& handle);
+  Status<void> Reply(const RemoteHandle& handle);
+  Status<void> Reply(const LocalChannelHandle& handle);
+  Status<void> Reply(const BorrowedChannelHandle& handle);
+  Status<void> Reply(const RemoteChannelHandle& handle);
+
+  template <typename T>
+  inline Status<void> Reply(const Status<T>& status) {
+    return status ? Reply(status.get()) : ReplyError(status.error());
+  }
+
+  inline Status<void> Reply(const Status<void>& status) {
+    return status ? Reply(0) : ReplyError(status.error());
+  }
+
+  /*
+   * Update the channel event bits with the given clear and set masks.
+   */
+  Status<void> ModifyChannelEvents(int clear_mask, int set_mask);
+
+  /*
+   * Create a new channel and push it as a file descriptor to the client. See
+   * Service::PushChannel() for a detail description of this method's operation.
+   */
+  Status<RemoteChannelHandle> PushChannel(
+      int flags, const std::shared_ptr<Channel>& channel, int* channel_id);
+
+  /*
+   * Create a new channel and push it as a file descriptor to the client. See
+   * Service::PushChannel() for a detail description of this method's operation.
+   */
+  Status<RemoteChannelHandle> PushChannel(
+      Service* service, int flags, const std::shared_ptr<Channel>& channel,
+      int* channel_id);
+
+  /*
+   * Check whether the |ref| is a reference to channel to this service.
+   * If the channel reference in question is valid, the Channel object is
+   * returned in |channel| when non-nullptr.
+   *
+   * Return values:
+   *  channel_id - id of the channel if the |ref| is a valid reference to
+   *               this service's channel.
+   * Errors:
+   *  EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+   *  another service.
+   *  EBADF - the file descriptor is invalid.
+   *  FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+   *  memory addresses.
+   *  EINVAL - the value of |ref| is invalid or the message id for this
+   *           message is no longer valid.
+   */
+  Status<int> CheckChannel(ChannelReference ref,
+                           std::shared_ptr<Channel>* channel) const;
+
+  /*
+   * Overload of CheckChannel() that checks whether the channel reference is for
+   * a channel to the service |service|.
+   */
+  Status<int> CheckChannel(const Service* service, ChannelReference ref,
+                           std::shared_ptr<Channel>* channel) const;
+
+  /*
+   * Overload of CheckChannel() that automatically converts to shared pointers
+   * to types derived from Channel.
+   */
+  template <class C>
+  Status<int> CheckChannel(ChannelReference ref,
+                           std::shared_ptr<C>* channel) const {
+    std::shared_ptr<Channel> base_pointer;
+    const Status<int> ret =
+        CheckChannel(ref, channel ? &base_pointer : nullptr);
+    if (channel)
+      *channel = std::static_pointer_cast<C>(base_pointer);
+    return ret;
+  }
+
+  template <class C>
+  Status<int> CheckChannel(const Service* service, ChannelReference ref,
+                           std::shared_ptr<C>* channel) const {
+    std::shared_ptr<Channel> base_pointer;
+    const Status<int> ret =
+        CheckChannel(service, ref, channel ? &base_pointer : nullptr);
+    if (channel)
+      *channel = std::static_pointer_cast<C>(base_pointer);
+    return ret;
+  }
+
+  /*
+   * MessageInfo accessors.
+   */
+  pid_t GetProcessId() const;
+  pid_t GetThreadId() const;
+  uid_t GetEffectiveUserId() const;
+  gid_t GetEffectiveGroupId() const;
+  int GetChannelId() const;
+  int GetMessageId() const;
+  int GetOp() const;
+  int GetFlags() const;
+  size_t GetSendLength() const;
+  size_t GetReceiveLength() const;
+  size_t GetFileDescriptorCount() const;
+
+  /*
+   * Impulses are asynchronous and cannot be replied to. All impulses have this
+   * invalid message id.
+   */
+  enum { IMPULSE_MESSAGE_ID = -1 };
+
+  /*
+   * Returns true if this Message describes an asynchronous "impulse" message.
+   */
+  bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; }
+
+  /*
+   * Returns a pointer to the impulse payload. Impulses are a maximum of 32
+   * bytes in size and the start of the impulse payload is guaranteed to be
+   * 8-byte aligned. Use GetSendLength() to determine the payload size.
+   */
+  const std::uint8_t* ImpulseBegin() const;
+
+  /*
+   * Returns one byte past the end of the impulse payload, as conventional for
+   * STL iterators.
+   */
+  const std::uint8_t* ImpulseEnd() const;
+
+  /*
+   * Get/set the Channel object for the channel associated
+   * with this message. It is up to the caller to synchronize
+   * these in multi-threaded services.
+   */
+  std::shared_ptr<Channel> GetChannel() const;
+  Status<void> SetChannel(const std::shared_ptr<Channel>& channnel);
+
+  /*
+   * Get the Channel object for the channel associated with this message,
+   * automatically converted to the desired subclass of Channel.
+   */
+  template <class C>
+  std::shared_ptr<C> GetChannel() const {
+    return std::static_pointer_cast<C>(GetChannel());
+  }
+
+  /*
+   * Gets the service this message was received on. Returns nullptr if the
+   * service was destroyed.
+   */
+  std::shared_ptr<Service> GetService() const;
+
+  /*
+   * Raw access to the MessageInfo for this message.
+   */
+  const MessageInfo& GetInfo() const;
+
+  bool replied() const { return replied_; }
+  bool IsChannelExpired() const { return channel_.expired(); }
+  bool IsServiceExpired() const { return service_.expired(); }
+
+  /*
+   * Returns true if the message is non-empty; that is the message can be
+   * replied to using this instance.
+   */
+  explicit operator bool() const { return !replied_; }
+
+  const void* GetState() const { return state_; }
+  void* GetState() { return state_; }
+
+ private:
+  friend class Service;
+
+  Message(const Message&) = delete;
+  void operator=(const Message&) = delete;
+  void Destroy();
+
+  std::weak_ptr<Service> service_;
+  std::weak_ptr<Channel> channel_;
+  MessageInfo info_;
+  void* state_{nullptr};
+  bool replied_;
+};
+
+// Base class for RPC services.
+class Service : public std::enable_shared_from_this<Service> {
+ public:
+  Service(const std::string& name, std::unique_ptr<Endpoint> endpoint);
+  virtual ~Service();
+
+  /*
+   * Utility to get a shared_ptr reference from the service context pointer.
+   */
+  static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info);
+
+  /*
+   * Returns whether initialization was successful. Subclasses that override
+   * this must call this base method and AND the results with their own. This
+   * method is not intended to do any initialization work itself, only to
+   * signal success or failure.
+   */
+  virtual bool IsInitialized() const;
+
+  /*
+   * Called by defaultHandleMessage in response to a CHANNEL_OPEN message.
+   * This gives subclasses of Service a convenient hook to create per-channel
+   * context in the form of a Channel subclass.
+   *
+   * The Channel instance returned by this is used to set the channel context
+   * pointer for the channel that was just opened.
+   */
+  virtual std::shared_ptr<Channel> OnChannelOpen(Message& message);
+
+  /*
+   * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message.
+   * This give subclasses of Service a convenient hook to clean up per-channel
+   * context.
+   */
+  virtual void OnChannelClose(Message& message,
+                              const std::shared_ptr<Channel>& channel);
+
+  /*
+   * Set the channel context for the given channel. This keeps a reference to
+   * the Channel object until the channel is closed or another call replaces
+   * the current value.
+   */
+  Status<void> SetChannel(int channel_id,
+                          const std::shared_ptr<Channel>& channel);
+
+  /*
+   * Get the channel context for the given channel id. This method should be
+   * used sparingly because of the performance characteristics of the underlying
+   * map; it is intended for limited, non-critical path access from outside of
+   * message dispatch. In most cases lookup by id should be unnecessary in a
+   * properly designed service; Message::GetChannel() should be used instead
+   * whenever an operation is in the context of a message.
+   *
+   * Again, if you lookup a channel context object for a service by id in a
+   * message handling path for the same service, you're probably doing something
+   * wrong.
+   */
+  std::shared_ptr<Channel> GetChannel(int channel_id) const;
+
+  /*
+   * Get a snapshot of the active channels for this service. This is the
+   * preferred way to access the set of channels because it avoids potential
+   * deadlocks and race conditions that may occur when operating on the channel
+   * map directly. However, it is more expensive than direct iteration because
+   * of dynamic memory allocation and shared pointer reference costs.
+   *
+   * Automatically converts returned items to shared pointers of the type
+   * std::shared_ptr<C>, where C is the subclass of Channel used by the service.
+   */
+  template <class C>
+  std::vector<std::shared_ptr<C>> GetChannels() const {
+    std::lock_guard<std::mutex> autolock(channels_mutex_);
+    std::vector<std::shared_ptr<C>> items;
+    items.reserve(channels_.size());
+
+    for (const auto& pair : channels_) {
+      items.push_back(std::static_pointer_cast<C>(pair.second));
+    }
+
+    return items;
+  }
+
+  /*
+   * Close a channel, signaling the client file object and freeing the channel
+   * id. Once closed, the client side of the channel always returns the error
+   * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+   *
+   * The internal reference to the Channel instance associated with the channel
+   * is removed, which may result in the Channel object being freed.
+   *
+   * OnChannelClosed is not called in response to this method call.
+   */
+  Status<void> CloseChannel(int channel_id);
+
+  /*
+   * Update the event bits for the given channel (given by id), using the
+   * given clear and set masks.
+   *
+   * This is useful for asynchronously signaling events that clients may be
+   * waiting for using select/poll/epoll.
+   */
+  Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+                                   int set_mask);
+
+  /*
+   * Create a new channel and push it as a file descriptor to the process
+   * sending the |message|. |flags| may be set to O_NONBLOCK and/or
+   * O_CLOEXEC to control the initial behavior of the new file descriptor (the
+   * sending process may change these later using fcntl()). The internal Channel
+   * instance associated with this channel is set to |channel|, which may be
+   * nullptr. The new channel id allocated for this channel is returned in
+   * |channel_id|, which may also be nullptr if not needed.
+   *
+   * On success, returns the remote channel handle for the new channel in the
+   * sending process' handle space. This MUST be returned to the sender via
+   * Message::Reply(), Message::Write(), or Message::WriteVector().
+   *
+   * On error, returns an errno code describing the cause of the error.
+   *
+   * Service::OnChannelCreate() is not called in response to the creation of the
+   * new channel.
+   */
+  Status<RemoteChannelHandle> PushChannel(
+      Message* message, int flags, const std::shared_ptr<Channel>& channel,
+      int* channel_id);
+
+  /*
+   * Check whether the |ref| is a reference to a channel to this service.
+   * If the channel reference in question is valid, the Channel object is
+   * returned in |channel| when non-nullptr.
+   *
+   * Return values:
+   *  channel_id - id of the channel if the channel reference.
+   * Errors:
+   *  EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+   *  another service.
+   *  EBADF - the file descriptor is invalid.
+   *  FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+   *  memory addresses.
+   *  EINVAL - the value of |ref| is invalid or the message id for this
+   *  message is no longer valid.
+   */
+  Status<int> CheckChannel(const Message* message, ChannelReference ref,
+                           std::shared_ptr<Channel>* channel) const;
+
+  /*
+   * Overload of CheckChannel() that automatically converts to shared pointers
+   * of types derived from Channel.
+   */
+  template <class C>
+  Status<int> CheckChannel(const Message* message, ChannelReference ref,
+                           std::shared_ptr<C>* channel) const {
+    std::shared_ptr<Channel> base_pointer;
+    const Status<int> ret =
+        CheckChannel(message, ref, channel ? &base_pointer : nullptr);
+    if (channel)
+      *channel = std::static_pointer_cast<C>(base_pointer);
+    return ret;
+  }
+
+  /*
+   * Handle a message. Subclasses override this to receive messages and decide
+   * how to dispatch them.
+   *
+   * The default implementation simply calls defaultHandleMessage().
+   * Subclasses should call the same for any unrecognized message opcodes.
+   */
+  virtual Status<void> HandleMessage(Message& message);
+
+  /*
+   * Handle an asynchronous message. Subclasses override this to receive
+   * asynchronous "impulse" messages. Impulses have a limited-size payload that
+   * is transferred upfront with the message description.
+   */
+  virtual void HandleImpulse(Message& impulse);
+
+  /*
+   * The default message handler. It is important that all messages
+   * (eventually) get a reply. This method should be called by subclasses for
+   * any unrecognized opcodes or otherwise unhandled messages to prevent
+   * erroneous requests from blocking indefinitely.
+   *
+   * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling
+   * OnChannelOpen() and OnChannelClose(), respectively.
+   *
+   * For all other message opcodes, this method replies with ENOTSUP.
+   */
+  Status<void> DefaultHandleMessage(Message& message);
+
+  /*
+   * Called when system properties have changed. Subclasses should implement
+   * this method if they need to handle when system properties change.
+   */
+  virtual void OnSysPropChange();
+
+  /*
+   * Get the endpoint for the service.
+   */
+  Endpoint* endpoint() const { return endpoint_.get(); }
+
+  /*
+   * Cancels the endpoint, unblocking any receiver threads waiting in
+   * ReceiveAndDispatch().
+   */
+  Status<void> Cancel();
+
+  /*
+   * Iterator type for Channel map iterators.
+   */
+  using ChannelIterator =
+      std::unordered_map<int, std::shared_ptr<Channel>>::iterator;
+
+  /*
+   * Iterates over the Channel map and performs the action given by |action| on
+   * each channel map item (const ChannelIterator::value_type).
+   * |channels_mutex_| is not held; it is the responsibility of the caller to
+   * ensure serialization between threads that modify or iterate over the
+   * Channel map.
+   */
+  template <class A>
+  void ForEachChannelUnlocked(A action) const {
+    std::for_each(channels_.begin(), channels_.end(), action);
+  }
+
+  /*
+   * Iterates over the Channel map and performs the action given by |action| on
+   * each channel map item (const ChannelIterator::value_type).
+   * |channels_mutex_| is held to serialize access to the map; care must be
+   * taken to avoid recursively acquiring the mutex, for example, by calling
+   * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or
+   * Message::SetChannel() in the action.
+   */
+  template <class A>
+  void ForEachChannel(A action) const {
+    std::lock_guard<std::mutex> autolock(channels_mutex_);
+    ForEachChannelUnlocked(action);
+  }
+
+  /*
+   * Subclasses of Service may override this method to provide a text string
+   * describing the state of the service. This method is called by
+   * HandleSystemMessage in response to the standard
+   * DUMP_STATE message. The string returned to the dump state client is
+   * truncated to |max_length| and reflects the maximum size the client can
+   * handle.
+   */
+  virtual std::string DumpState(size_t max_length);
+
+  /*
+   * Receives a message on this Service instance's endpoint and dispatches it.
+   * If the endpoint is in blocking mode this call blocks until a message is
+   * received, a signal is delivered to this thread, or the service is canceled.
+   * If the endpoint is in non-blocking mode and a message is not pending this
+   * call returns immediately with ETIMEDOUT.
+   */
+  Status<void> ReceiveAndDispatch();
+
+ private:
+  friend class Message;
+
+  Status<void> HandleSystemMessage(Message& message);
+
+  Service(const Service&);
+  void operator=(const Service&) = delete;
+
+  const std::string name_;
+  std::unique_ptr<Endpoint> endpoint_;
+
+  /*
+   * Maintains references to active channels.
+   */
+  mutable std::mutex channels_mutex_;
+  std::unordered_map<int, std::shared_ptr<Channel>> channels_;
+};
+
+/*
+ * Utility base class for services. This template handles allocation and
+ * initialization checks, reducing boiler plate code.
+ */
+template <typename TYPE>
+class ServiceBase : public Service {
+ public:
+  /*
+   * Static service allocation method that check for initialization errors.
+   * If errors are encountered these automatically clean up and return
+   * nullptr.
+   */
+  template <typename... Args>
+  static inline std::shared_ptr<TYPE> Create(Args&&... args) {
+    std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...));
+    if (service->IsInitialized())
+      return service;
+    else
+      return nullptr;
+  }
+
+ protected:
+  /*
+   * Shorthand for subclasses to refer to this base, particularly
+   * to call the base class constructor.
+   */
+  typedef ServiceBase<TYPE> BASE;
+
+  ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+      : Service(name, std::move(endpoint)) {}
+};
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+#define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]"
+
+/*
+ * Macros for replying to messages. Error handling can be tedious;
+ * these macros make things a little cleaner.
+ */
+#define CHECK_ERROR(cond, error, fmt, ...) \
+  do {                                     \
+    if ((cond)) {                          \
+      ALOGE(fmt, ##__VA_ARGS__);           \
+      goto error;                          \
+    }                                      \
+  } while (0)
+
+#define REPLY_ERROR(message, error, error_label)                              \
+  do {                                                                        \
+    auto __status = message.ReplyError(error);                                \
+    CHECK_ERROR(!__status, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                __status.GetErrorMessage().c_str());                          \
+    goto error_label;                                                         \
+  } while (0)
+
+#define REPLY_ERROR_RETURN(message, error, ...)                          \
+  do {                                                                   \
+    auto __status = message.ReplyError(error);                           \
+    ALOGE_IF(!__status,                                                  \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             __status.GetErrorMessage().c_str());                        \
+    return __VA_ARGS__;                                                  \
+  } while (0)
+
+#define REPLY_MESSAGE(message, message_return_code, error_label)              \
+  do {                                                                        \
+    auto __status = message.Reply(message_return_code);                       \
+    CHECK_ERROR(!__status, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                __status.GetErrorMessage().c_str());                          \
+    goto error_label;                                                         \
+  } while (0)
+
+#define REPLY_SUCCESS(message, message_return_code, error_label) \
+  REPLY_MESSAGE(message, message_return_code, error_label)
+
+#define REPLY_MESSAGE_RETURN(message, message_return_code, ...)          \
+  do {                                                                   \
+    auto __status = message.Reply(message_return_code);                  \
+    ALOGE_IF(!__status,                                                  \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             __status.GetErrorMessage().c_str());                        \
+    return __VA_ARGS__;                                                  \
+  } while (0)
+
+#define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \
+  REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__)
+
+#define REPLY_FD(message, push_fd, error_label)                               \
+  do {                                                                        \
+    auto __status = message.ReplyFileDescriptor(push_fd);                     \
+    CHECK_ERROR(!__status, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                __status.GetErrorMessage().c_str());                          \
+    goto error_label;                                                         \
+  } while (0)
+
+#define REPLY_FD_RETURN(message, push_fd, ...)                           \
+  do {                                                                   \
+    auto __status = message.ReplyFileDescriptor(push_fd);                \
+    ALOGE_IF(__status < 0,                                               \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             __status.GetErrorMessage().c_str());                        \
+    return __VA_ARGS__;                                                  \
+  } while (0)
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_SERVICE_H_
diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h
new file mode 100644
index 0000000..c5e342a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_PDX_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_SERVICE_DISPATCHER_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+/*
+ * ServiceDispatcher manages a list of Service instances and handles message
+ * reception and dispatch to the services. This makes repetitive dispatch tasks
+ * easier to implement.
+ */
+class ServiceDispatcher {
+ public:
+  virtual ~ServiceDispatcher() = default;
+
+  /*
+   * Adds a service to the list of services handled by this dispatcher. This
+   * will fail if any threads are blocked waiting for messages in this
+   * dispatcher.
+   *
+   * Returns 0 on success; -EEXIST if the service was already added.
+   */
+  virtual int AddService(const std::shared_ptr<Service>& service) = 0;
+
+  /*
+   * Removes a service from this dispatcher. This will fail if any threads are
+   * blocked waiting for messages in this dispatcher.
+   *
+   * 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;
+
+  /*
+   * Receive and dispatch one set of messages. Multiple threads may enter this
+   * method to create an implicit thread pool, as described for
+   * enterDispatchLoop() below, however this method exits after one dispatch
+   * cycle, requiring an external loop. This is useful when other work needs
+   * to be done in the service dispatch loop.
+   */
+  virtual int ReceiveAndDispatch() = 0;
+
+  /*
+   * 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;
+
+  /*
+   * Receive and dispatch messages until canceled. When more than one thread
+   * enters this method it creates an implicit thread pool to dispatch messages.
+   * Explicit thread pools may be created by using a single dispatch thread that
+   * hands Message instances (via move assignment) over to a queue of threads
+   * (or perhaps one of several) to handle.
+   */
+  virtual int EnterDispatchLoop() = 0;
+
+  /*
+   * 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;
+
+  /*
+   * Gets the canceled state of the dispatcher.
+   */
+  virtual bool IsCanceled() const = 0;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h
new file mode 100644
index 0000000..28bd6bc
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -0,0 +1,144 @@
+#ifndef ANDROID_PDX_ENDPOINT_H_
+#define ANDROID_PDX_ENDPOINT_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class Service;
+class Channel;
+class Message;
+
+struct MessageInfo {
+  int pid{0};
+  int tid{0};
+  int cid{0};
+  int mid{0};
+  int euid{0};
+  int egid{0};
+  int32_t op{0};
+  uint32_t flags{0};
+  Service* service{nullptr};
+  Channel* channel{nullptr};
+  size_t send_len{0};
+  size_t recv_len{0};
+  size_t fd_count{0};
+  uint64_t impulse[4] = {};
+};
+
+// Wrapper around transport endpoint. Abstracts the underlying transport APIs in
+// a way, that the underlying IPC can be substituted for another technology
+// without changing the Service, Client and Message classes of this library.
+class Endpoint {
+ public:
+  virtual ~Endpoint() = default;
+
+  // Returns a tag that uniquely identifies a specific underlying IPC transport.
+  virtual uint32_t GetIpcTag() const = 0;
+
+  // Associates a Service instance with an endpoint by setting the service
+  // context pointer to the address of the Service. Only one Service may be
+  // associated with a given endpoint.
+  virtual Status<void> SetService(Service* service) = 0;
+
+  // Set the channel context for the given channel.
+  virtual Status<void> SetChannel(int channel_id, Channel* channel) = 0;
+
+  // Close a channel, signaling the client file object and freeing the channel
+  // id. Once closed, the client side of the channel always returns the error
+  // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+  virtual Status<void> CloseChannel(int channel_id) = 0;
+
+  // Update the event bits for the given channel (given by id), using the
+  // given clear and set masks.
+  virtual Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+                                           int set_mask) = 0;
+
+  // Create a new channel and push it as a file descriptor to the process
+  // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+  // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+  // sending process may change these later using fcntl()). The internal Channel
+  // instance associated with this channel is set to |channel|, which may be
+  // nullptr. The new channel id allocated for this channel is returned in
+  // |channel_id|, which may also be nullptr if not needed.
+  virtual Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+                                                  Channel* channel,
+                                                  int* channel_id) = 0;
+
+  // Check whether the |ref| is a reference to a channel to the service
+  // represented by the |endpoint|. If the channel reference in question is
+  // valid, the Channel object is returned in |channel| when non-nullptr and
+  // the channel ID is returned through the Status object.
+  virtual Status<int> CheckChannel(const Message* message, ChannelReference ref,
+                                   Channel** channel) = 0;
+
+  // Receives a message on the given endpoint file descriptor.
+  virtual Status<void> MessageReceive(Message* message) = 0;
+
+  // Replies to the message with a return code.
+  virtual Status<void> MessageReply(Message* message, int return_code) = 0;
+
+  // Replies to the message with a file descriptor.
+  virtual Status<void> MessageReplyFd(Message* message,
+                                      unsigned int push_fd) = 0;
+
+  // Replies to the message with a local channel handle.
+  virtual Status<void> MessageReplyChannelHandle(
+      Message* message, const LocalChannelHandle& handle) = 0;
+
+  // Replies to the message with a borrowed local channel handle.
+  virtual Status<void> MessageReplyChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) = 0;
+
+  // Replies to the message with a remote channel handle.
+  virtual Status<void> MessageReplyChannelHandle(
+      Message* message, const RemoteChannelHandle& handle) = 0;
+
+  // Reads message data into an array of memory buffers.
+  virtual Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+                                         size_t vector_length) = 0;
+
+  // Sends reply data for message.
+  virtual Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+                                          size_t vector_length) = 0;
+
+  // Records a file descriptor into the message buffer and returns the remapped
+  // reference to be sent to the remote process.
+  virtual Status<FileReference> PushFileHandle(Message* message,
+                                               const LocalHandle& handle) = 0;
+  virtual Status<FileReference> PushFileHandle(
+      Message* message, const BorrowedHandle& handle) = 0;
+  virtual Status<FileReference> PushFileHandle(Message* message,
+                                               const RemoteHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      Message* message, const LocalChannelHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) = 0;
+  virtual Status<ChannelReference> PushChannelHandle(
+      Message* message, const RemoteChannelHandle& handle) = 0;
+
+  // Obtains a file descriptor/channel handle from a message for the given
+  // reference.
+  virtual LocalHandle GetFileHandle(Message* message,
+                                    FileReference ref) const = 0;
+  virtual LocalChannelHandle GetChannelHandle(Message* message,
+                                              ChannelReference ref) const = 0;
+
+  // Transport-specific message state management.
+  virtual void* AllocateMessageState() = 0;
+  virtual void FreeMessageState(void* state) = 0;
+
+  // Cancels the endpoint, unblocking any receiver threads waiting for a
+  // message.
+  virtual Status<void> Cancel() = 0;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/status.h b/libs/vr/libpdx/private/pdx/status.h
new file mode 100644
index 0000000..067fe25
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/status.h
@@ -0,0 +1,180 @@
+#ifndef ANDROID_PDX_STATUS_H_
+#define ANDROID_PDX_STATUS_H_
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace pdx {
+
+// This is a helper class for constructing Status<T> with an error code.
+struct ErrorStatus {
+ public:
+  ErrorStatus(int error) : error_{error} {}
+  int error() const { return error_; }
+
+  static std::string ErrorToString(int error_code);
+
+ private:
+  int error_;
+};
+
+// Status<T> is a container class that can be used to return a value of type T
+// or error code to the caller.
+template <typename T>
+class Status {
+ public:
+  // Default constructor so an empty Status object can be created.
+  Status() : error_{-1} {}
+
+  // Value copy/move constructors. These are intentionally not marked as
+  // explicit to allow direct value returns from functions without having
+  // to explicitly wrap them into Status<T>().
+  Status(const T& value) : value_{value} {}        // NOLINT(runtime/explicit)
+  Status(T&& value) : value_{std::move(value)} {}  // NOLINT(runtime/explicit)
+
+  // Constructor for storing an error code inside the Status object.
+  Status(const ErrorStatus& error_status)  // NOLINT(runtime/explicit)
+      : error_{error_status.error()} {}
+
+  // Copy/move constructors. Move constructor leaves |other| object in empty
+  // state.
+  Status(const Status& other) = default;
+  Status(Status&& other)
+      : value_{std::move(other.value_)}, error_{other.error_} {
+    other.error_ = -1;
+  }
+
+  // Assignment operators.
+  Status& operator=(const Status& other) = default;
+  Status& operator=(Status&& other) {
+    error_ = other.error_;
+    value_ = std::move(other.value_);
+    other.error_ = -1;
+    T empty;
+    std::swap(other.value_, empty);
+    return *this;
+  }
+
+  // Change the value/error code of the status object directly.
+  void SetValue(T value) {
+    error_ = 0;
+    value_ = std::move(value);
+  }
+  void SetError(int error) {
+    error_ = error;
+    T empty;
+    std::swap(value_, empty);
+  }
+
+  // If |other| is in error state, copy the error code to this object.
+  // Returns true if error was propagated
+  template<typename U>
+  bool PropagateError(const Status<U>& other) {
+    if (!other.ok() && !other.empty()) {
+      SetError(other.error());
+      return true;
+    }
+    return false;
+  }
+
+  // Returns true if the status object contains valid value for type T.
+  // This means, the object is not empty and does not contain an error code.
+  bool ok() const { return error_ == 0; }
+
+  // Checks if the object is empty (doesn't contain a valid value nor an error).
+  bool empty() const { return error_ < 0; }
+
+  // Explicit bool conversion, equivalent to invoking ok().
+  explicit operator bool() const { return ok(); }
+
+  // Accessors for the value stored in Status. Calling when ok() is false leads
+  // to undefined behavior.
+  const T& get() const { return value_; }
+  T&& take() {
+    error_ = -1;
+    return std::move(value_);
+  }
+
+  // Returns the error code stored in the object. These codes are positive
+  // non-zero values.
+  // Can be called only when an error is actually stored (that is, the object
+  // is not empty nor containing a valid value).
+  int error() const { return std::max(error_, 0); }
+
+  // Returns the error code as ErrorStatus object. This is a helper method
+  // to aid in propagation of error codes between Status<T> of different types
+  // as in the following example:
+  //    Status<int> foo() {
+  //      Status<void> status = bar();
+  //      if(!status)
+  //        return status.error_status();
+  //      return 12;
+  //    }
+  inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
+
+  // Returns the error message associated with error code stored in the object.
+  // The message is the same as the string returned by strerror(status.error()).
+  // Can be called only when an error is actually stored (that is, the object
+  // is not empty nor containing a valid value).
+  std::string GetErrorMessage() const {
+    std::string message;
+    if (error_ > 0)
+      message = ErrorStatus::ErrorToString(error_);
+    return message;
+  }
+
+ private:
+  T value_{};
+  int error_{0};
+};
+
+// Specialization for status containing no other value but the error code.
+template <>
+class Status<void> {
+ public:
+  Status() = default;
+  Status(const ErrorStatus& error_status)  // NOLINT(runtime/explicit)
+      : error_{error_status.error()} {}
+  void SetValue() { error_ = 0; }
+  void SetError(int error) { error_ = error; }
+
+  template<typename U>
+  bool PropagateError(const Status<U>& other) {
+    if (!other.ok() && !other.empty()) {
+      SetError(other.error());
+      return true;
+    }
+    return false;
+  }
+
+  bool ok() const { return error_ == 0; }
+  bool empty() const { return false; }
+  explicit operator bool() const { return ok(); }
+  int error() const { return std::max(error_, 0); }
+  inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
+  std::string GetErrorMessage() const {
+    std::string message;
+    if (error_ > 0)
+      message = ErrorStatus::ErrorToString(error_);
+    return message;
+  }
+
+ private:
+  int error_{0};
+};
+
+// TODO(avakulenko): Remove these function once all callers of it are gone.
+inline int ReturnStatusOrError(const Status<void>& status) {
+  return status ? 0 : -status.error();
+}
+
+inline int ReturnStatusOrError(const Status<int>& status) {
+  return status ? status.get() : -status.error();
+}
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_STATUS_H_
diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h
new file mode 100644
index 0000000..ebe8491
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/trace.h
@@ -0,0 +1,35 @@
+#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.
+
+// 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
+#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
+
+#endif  // ANDROID_PDX_TRACE_H_
diff --git a/libs/vr/libpdx/private/pdx/utility.h b/libs/vr/libpdx/private/pdx/utility.h
new file mode 100644
index 0000000..305c3b8
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/utility.h
@@ -0,0 +1,367 @@
+#ifndef ANDROID_PDX_UTILITY_H_
+#define ANDROID_PDX_UTILITY_H_
+
+#include <cstdint>
+#include <iterator>
+
+#include <pdx/rpc/sequence.h>
+
+// Utilities for testing object serialization.
+
+namespace android {
+namespace pdx {
+
+class ByteBuffer {
+ public:
+  using iterator = uint8_t*;
+  using const_iterator = const uint8_t*;
+  using size_type = size_t;
+
+  ByteBuffer() = default;
+  ByteBuffer(const ByteBuffer& other) {
+    resize(other.size());
+    if (other.size())
+      memcpy(data_, other.data(), other.size());
+  }
+
+  ByteBuffer& operator=(const ByteBuffer& other) {
+    resize(other.size());
+    if (other.size())
+      memcpy(data_, other.data(), other.size());
+    return *this;
+  }
+
+  ByteBuffer& operator=(ByteBuffer&& other) {
+    std::swap(data_, other.data_);
+    std::swap(size_, other.size_);
+    std::swap(capacity_, other.capacity_);
+    other.clear();
+    return *this;
+  }
+
+  inline const uint8_t* data() const { return data_; }
+  inline uint8_t* data() { return data_; }
+  inline size_t size() const { return size_; }
+  inline size_t capacity() const { return capacity_; }
+
+  iterator begin() { return data_; }
+  const_iterator begin() const { return data_; }
+  iterator end() { return data_ + size_; }
+  const_iterator end() const { return data_ + size_; }
+
+  inline bool operator==(const ByteBuffer& other) const {
+    return size_ == other.size_ &&
+           (size_ == 0 || memcmp(data_, other.data_, size_) == 0);
+  }
+
+  inline bool operator!=(const ByteBuffer& other) const {
+    return !operator==(other);
+  }
+
+  inline void reserve(size_t size) {
+    if (size <= capacity_)
+      return;
+    // Find next power of 2 (assuming the size is 32 bits for now).
+    size--;
+    size |= size >> 1;
+    size |= size >> 2;
+    size |= size >> 4;
+    size |= size >> 8;
+    size |= size >> 16;
+    size++;
+    void* new_data = data_ ? realloc(data_, size) : malloc(size);
+    // TODO(avakulenko): Check for allocation failures.
+    data_ = static_cast<uint8_t*>(new_data);
+    capacity_ = size;
+  }
+
+  inline void resize(size_t size) {
+    reserve(size);
+    size_ = size;
+  }
+
+  inline uint8_t* grow_by(size_t size_delta) {
+    size_t old_size = size_;
+    resize(old_size + size_delta);
+    return data_ + old_size;
+  }
+
+  inline void clear() { size_ = 0; }
+
+ private:
+  uint8_t* data_{nullptr};
+  size_t size_{0};
+  size_t capacity_{0};
+};
+
+// Utility functions to increment/decrement void pointers to data buffers.
+template <typename OFFSET_T>
+inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) {
+  return static_cast<const uint8_t*>(ptr) + offset;
+}
+
+template <typename OFFSET_T>
+inline void* AdvancePointer(void* ptr, OFFSET_T offset) {
+  return static_cast<uint8_t*>(ptr) + offset;
+}
+
+inline ptrdiff_t PointerDistance(const void* end, const void* begin) {
+  return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin);
+}
+
+// Utility to build sequences of types.
+template <typename, typename>
+struct AppendTypeSequence;
+
+template <typename T, typename... S, template <typename...> class TT>
+struct AppendTypeSequence<T, TT<S...>> {
+  using type = TT<S..., T>;
+};
+
+// Utility to generate repeated types.
+template <typename T, std::size_t N, template <typename...> class TT>
+struct RepeatedType {
+  using type = typename AppendTypeSequence<
+      T, typename RepeatedType<T, N - 1, TT>::type>::type;
+};
+
+template <typename T, template <typename...> class TT>
+struct RepeatedType<T, 0, TT> {
+  using type = TT<>;
+};
+
+template <typename V, typename S>
+inline V ReturnValueHelper(V value, S /*ignore*/) {
+  return value;
+}
+
+template <typename R, typename V, size_t... S>
+inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) {
+  return std::make_tuple(ReturnValueHelper(value, S)...);
+}
+
+// Returns an N-tuple of type std::tuple<T,...T> containing |value| in each
+// element.
+template <size_t N, typename T,
+          typename R = typename RepeatedType<T, N, std::tuple>::type>
+inline R GetNTuple(T value) {
+  return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{});
+}
+
+class NoOpOutputResourceMapper : public OutputResourceMapper {
+ public:
+  Status<FileReference> PushFileHandle(const LocalHandle& handle) override {
+    return handle.Get();
+  }
+
+  Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override {
+    return handle.Get();
+  }
+
+  Status<FileReference> PushFileHandle(const RemoteHandle& handle) override {
+    return handle.Get();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) override {
+    return handle.value();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) override {
+    return handle.value();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) override {
+    return handle.value();
+  }
+};
+
+class NoOpInputResourceMapper : public InputResourceMapper {
+ public:
+  bool GetFileHandle(FileReference ref, LocalHandle* handle) override {
+    *handle = LocalHandle{ref};
+    return true;
+  }
+
+  bool GetChannelHandle(ChannelReference ref,
+                        LocalChannelHandle* handle) override {
+    *handle = LocalChannelHandle{nullptr, ref};
+    return true;
+  }
+};
+
+class NoOpResourceMapper : public NoOpOutputResourceMapper,
+                           public NoOpInputResourceMapper {};
+
+// Simple implementation of the payload interface, required by
+// Serialize/Deserialize. This is intended for test cases, where compatibility
+// with std::vector is helpful.
+class Payload : public MessageWriter,
+                public MessageReader,
+                public OutputResourceMapper {
+ public:
+  using BaseType = ByteBuffer;
+  using iterator = typename BaseType::iterator;
+  using const_iterator = typename BaseType::const_iterator;
+  using size_type = typename BaseType::size_type;
+
+  Payload() = default;
+  explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); }
+  Payload(const Payload& other) : buffer_(other.buffer_) {}
+  Payload(const std::initializer_list<uint8_t>& initializer) {
+    buffer_.resize(initializer.size());
+    std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+  }
+
+  Payload& operator=(const Payload& other) {
+    buffer_ = other.buffer_;
+    read_pos_ = 0;
+    return *this;
+  }
+  Payload& operator=(const std::initializer_list<uint8_t>& initializer) {
+    buffer_.resize(initializer.size());
+    std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+    read_pos_ = 0;
+    return *this;
+  }
+
+  // Compares Payload with Payload.
+  bool operator==(const Payload& other) const {
+    return buffer_ == other.buffer_;
+  }
+  bool operator!=(const Payload& other) const {
+    return buffer_ != other.buffer_;
+  }
+
+  // Compares Payload with std::vector.
+  template <typename Type, typename AllocatorType>
+  typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+  operator==(const std::vector<Type, AllocatorType>& other) const {
+    return buffer_.size() == other.size() &&
+           memcmp(buffer_.data(), other.data(), other.size()) == 0;
+  }
+  template <typename Type, typename AllocatorType>
+  typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+  operator!=(const std::vector<Type, AllocatorType>& other) const {
+    return !operator!=(other);
+  }
+
+  iterator begin() { return buffer_.begin(); }
+  const_iterator begin() const { return buffer_.begin(); }
+  iterator end() { return buffer_.end(); }
+  const_iterator end() const { return buffer_.end(); }
+
+  void Append(size_type count, uint8_t value) {
+    auto* data = buffer_.grow_by(count);
+    std::fill(data, data + count, value);
+  }
+
+  void Clear() {
+    buffer_.clear();
+    file_handles_.clear();
+    read_pos_ = 0;
+  }
+
+  void Rewind() { read_pos_ = 0; }
+
+  uint8_t* Data() { return buffer_.data(); }
+  const uint8_t* Data() const { return buffer_.data(); }
+  size_type Size() const { return buffer_.size(); }
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override {
+    return buffer_.grow_by(size);
+  }
+
+  OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+  // OutputResourceMapper
+  Status<FileReference> PushFileHandle(const LocalHandle& handle) override {
+    if (handle) {
+      const int ref = file_handles_.size();
+      file_handles_.push_back(handle.Get());
+      return ref;
+    } else {
+      return handle.Get();
+    }
+  }
+
+  Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override {
+    if (handle) {
+      const int ref = file_handles_.size();
+      file_handles_.push_back(handle.Get());
+      return ref;
+    } else {
+      return handle.Get();
+    }
+  }
+
+  Status<FileReference> PushFileHandle(const RemoteHandle& handle) override {
+    return handle.Get();
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) override {
+    if (handle) {
+      const int ref = file_handles_.size();
+      file_handles_.push_back(handle.value());
+      return ref;
+    } else {
+      return handle.value();
+    }
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) override {
+    if (handle) {
+      const int ref = file_handles_.size();
+      file_handles_.push_back(handle.value());
+      return ref;
+    } else {
+      return handle.value();
+    }
+  }
+
+  Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) override {
+    return handle.value();
+  }
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override {
+    return {buffer_.data() + read_pos_, &*buffer_.end()};
+  }
+
+  void ConsumeReadBufferSectionData(const void* new_start) override {
+    read_pos_ = PointerDistance(new_start, buffer_.data());
+  }
+
+  InputResourceMapper* GetInputResourceMapper() override {
+    return &input_resource_mapper_;
+  }
+
+  const int* FdArray() const { return file_handles_.data(); }
+  std::size_t FdCount() const { return file_handles_.size(); }
+
+ private:
+  NoOpInputResourceMapper input_resource_mapper_;
+  ByteBuffer buffer_;
+  std::vector<int> file_handles_;
+  size_t read_pos_{0};
+};
+
+}  // namespace pdx
+}  // namespace android
+
+// Helper macros for branch prediction hinting.
+#ifdef __GNUC__
+#define PDX_LIKELY(x) __builtin_expect(!!(x), true)
+#define PDX_UNLIKELY(x) __builtin_expect(!!(x), false)
+#else
+#define PDX_LIKELY(x) (x)
+#define PDX_UNLIKELY(x) (x)
+#endif
+
+#endif  // ANDROID_PDX_UTILITY_H_
diff --git a/libs/vr/libpdx/serialization_tests.cpp b/libs/vr/libpdx/serialization_tests.cpp
new file mode 100644
index 0000000..5ad1047
--- /dev/null
+++ b/libs/vr/libpdx/serialization_tests.cpp
@@ -0,0 +1,2505 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+// Tests the serialization/deserialization of all supported types, verifying all
+// reasonable boundary conditions for types with multiple encodings.
+//
+// NOTE: Sometimes this file uses the construct "var = decltype(var)({...})"
+// instead of the equivalent "var = {...}" to construct vectors. This is to
+// prevent clang-format from producing annoyingly vertical code from long
+// initializers.
+
+// TODO(eieio): Automatically generate some of these tests?
+
+namespace {
+
+// Test data for serialization/deserialization of floats.
+const float kZeroFloat = 0.0f;
+const float kOneFloat = 1.0f;
+const auto kZeroFloatBytes = reinterpret_cast<const std::uint8_t*>(&kZeroFloat);
+const auto kOneFloatBytes = reinterpret_cast<const std::uint8_t*>(&kOneFloat);
+const double kZeroDouble = 0.0;
+const double kOneDouble = 1.0;
+const auto kZeroDoubleBytes =
+    reinterpret_cast<const std::uint8_t*>(&kZeroDouble);
+const auto kOneDoubleBytes = reinterpret_cast<const std::uint8_t*>(&kOneDouble);
+
+struct TestType {
+  enum class Foo { kFoo, kBar, kBaz };
+
+  int a;
+  float b;
+  std::string c;
+  Foo d;
+
+  TestType() {}
+  TestType(int a, float b, const std::string& c, Foo d)
+      : a(a), b(b), c(c), d(d) {}
+
+  // Make gtest expressions simpler by defining equality operator. This is not
+  // needed for serialization.
+  bool operator==(const TestType& other) const {
+    return a == other.a && b == other.b && c == other.c && d == other.d;
+  }
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c, d);
+};
+
+template <typename FileHandleType>
+struct TestTemplateType {
+  FileHandleType fd;
+
+  TestTemplateType() {}
+  TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+  bool operator==(const TestTemplateType& other) const {
+    return fd.Get() == other.fd.Get();
+  }
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+// Utilities to generate test maps and payloads.
+template <typename MapType>
+MapType MakeMap(std::size_t size) {
+  MapType result;
+  for (std::size_t i = 0; i < size; i++) {
+    result.emplace(i, i);
+  }
+  return result;
+}
+
+template <typename MapType>
+void InsertKeyValue(MessageWriter* writer, std::size_t size) {
+  MapType map;
+  for (std::size_t i = 0; i < size; i++) {
+    map.emplace(i, i);
+  }
+  for (const auto& element : map) {
+    Serialize(element.first, writer);
+    Serialize(element.second, writer);
+  }
+}
+
+}  // anonymous namespace
+
+TEST(SerializableTypes, Constructor) {
+  TestType tt(1, 2.0, "three", TestType::Foo::kBar);
+  EXPECT_EQ(1, tt.a);
+  EXPECT_EQ(2.0, tt.b);
+  EXPECT_EQ("three", tt.c);
+  EXPECT_EQ(TestType::Foo::kBar, tt.d);
+}
+
+TEST(SerializationTest, bool) {
+  Payload result;
+  Payload expected;
+  bool value;
+
+  // True.
+  value = true;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_TRUE};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // False.
+  value = false;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FALSE};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, uint8_t) {
+  Payload result;
+  Payload expected;
+  uint8_t value;
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = (1 << 7) - 1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT8.
+  value = (1 << 7);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT8.
+  value = 0xff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, uint16_t) {
+  Payload result;
+  Payload expected;
+  uint16_t value;
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = (1 << 7) - 1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT8.
+  value = (1 << 7);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT8.
+  value = 0xff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT16.
+  value = (1 << 8);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0, 1};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT16.
+  value = 0xffff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, uint32_t) {
+  Payload result;
+  Payload expected;
+  uint32_t value;
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = (1 << 7) - 1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT8.
+  value = (1 << 7);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT8.
+  value = 0xff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT16.
+  value = (1 << 8);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0, 1};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT16.
+  value = 0xffff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT32.
+  value = (1 << 16);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT32.
+  value = 0xffffffff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, uint64_t) {
+  Payload result;
+  Payload expected;
+  uint64_t value;
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = (1 << 7) - 1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT8.
+  value = (1 << 7);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT8.
+  value = 0xff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT8, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT16.
+  value = (1 << 8);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0, 1};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT16.
+  value = 0xffff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT32.
+  value = (1 << 16);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT32.
+  value = 0xffffffff;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min UINT64.
+  value = (1ULL << 32);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_UINT64, 0, 0, 0, 0, 1, 0, 0, 0};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max UINT64.
+  value = 0xffffffffffffffffULL;
+  Serialize(value, &result);
+  expected = {
+      ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, int8_t) {
+  Payload result;
+  Payload expected;
+  int8_t value;
+
+  // Min NEGATIVE FIXINT.
+  value = -32;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max NEGATIVE FIXINT.
+  value = -1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = 127;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT8.
+  value = -128;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT8.
+  value = -33;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0xdf};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, int16_t) {
+  Payload result;
+  Payload expected;
+  int16_t value;
+
+  // Min NEGATIVE FIXINT.
+  value = -32;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max NEGATIVE FIXINT.
+  value = -1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = 127;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT8.
+  value = -128;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT8.
+  value = -33;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0xdf};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT16.
+  value = -32768;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT16.
+  value = 32767;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, int32_t) {
+  Payload result;
+  Payload expected;
+  int32_t value;
+
+  // Min NEGATIVE FIXINT.
+  value = -32;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max NEGATIVE FIXINT.
+  value = -1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = 127;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT8.
+  value = -128;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT8.
+  value = -33;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0xdf};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT16.
+  value = -32768;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT16.
+  value = 32767;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT32.
+  value = -2147483648;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT32.
+  value = 2147483647;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, int64_t) {
+  Payload result;
+  Payload expected;
+  int64_t value;
+
+  // Min NEGATIVE FIXINT.
+  value = -32;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max NEGATIVE FIXINT.
+  value = -1;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min FIXINT.
+  value = 0;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXINT.
+  value = 127;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT8.
+  value = -128;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT8.
+  value = -33;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT8, 0xdf};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT16.
+  value = -32768;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT16.
+  value = 32767;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT32.
+  value = -2147483648;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT32.
+  value = 2147483647;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min INT64.
+  value = -9223372036854775808ULL;
+  Serialize(value, &result);
+  expected = {
+      ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max INT64.
+  value = 9223372036854775807ULL;
+  Serialize(value, &result);
+  expected = {
+      ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, float) {
+  Payload result;
+  Payload expected;
+  float value;
+
+  value = 0.0f;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+              kZeroFloatBytes[2], kZeroFloatBytes[3]};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  value = 1.0f;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+              kOneFloatBytes[2], kOneFloatBytes[3]};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, double) {
+  Payload result;
+  Payload expected;
+  double value;
+
+  value = 0.0f;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+              kZeroDoubleBytes[2],   kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+              kZeroDoubleBytes[5],   kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  value = 1.0f;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+              kOneDoubleBytes[2],    kOneDoubleBytes[3], kOneDoubleBytes[4],
+              kOneDoubleBytes[5],    kOneDoubleBytes[6], kOneDoubleBytes[7]};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, Enum) {
+  Payload result;
+  Payload expected;
+
+  enum Foo { kFoo, kBar, kBaz };
+  Foo value = kBar;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, EnumClass) {
+  Payload result;
+  Payload expected;
+
+  enum class Foo { kFoo, kBar, kBaz };
+  Foo value = Foo::kBaz;
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, LocalHandle) {
+  Payload result;
+  Payload expected;
+  LocalHandle fd1;
+  LocalHandle fd2;
+
+  fd1 = LocalHandle(100);
+  Serialize(fd1, &result);
+  expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+  EXPECT_EQ(expected, result);
+  EXPECT_EQ(1u, result.FdCount());
+  EXPECT_EQ(100, result.FdArray()[0]);
+  result.Clear();
+
+  fd2 = LocalHandle(200);
+  Serialize(std::forward_as_tuple(fd1, fd2), &result);
+  expected = decltype(expected)(
+      {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+       ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+       ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+  EXPECT_EQ(expected, result);
+  EXPECT_EQ(2u, result.FdCount());
+  EXPECT_EQ(100, result.FdArray()[0]);
+  EXPECT_EQ(200, result.FdArray()[1]);
+  result.Clear();
+
+  fd1.Release();  // Don't try to close fd 100.
+  fd2.Release();  // Don't try to close fd 200.
+
+  fd1 = LocalHandle(-2);
+  Serialize(fd1, &result);
+  expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+              0xff};
+  EXPECT_EQ(expected, result);
+  EXPECT_EQ(0u, result.FdCount());
+  result.Clear();
+}
+
+TEST(SerializationTest, string) {
+  Payload result;
+  Payload expected;
+  std::string value;
+
+  // Min FIXSTR.
+  value = "";
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXSTR_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXSTR.
+  value = std::string((1 << 5) - 1, 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXSTR_MAX};
+  expected.Append((1 << 5) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR8.
+  value = std::string((1 << 5), 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_STR8, (1 << 5)};
+  expected.Append((1 << 5), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max STR8.
+  value = std::string((1 << 8) - 1, 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+  expected.Append((1 << 8) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR16.
+  value = std::string((1 << 8), 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+  expected.Append((1 << 8), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max STR16.
+  value = std::string((1 << 16) - 1, 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+  expected.Append((1 << 16) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR32.
+  value = std::string((1 << 16), 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, StringWrapper) {
+  Payload result;
+  Payload expected;
+  std::string value;
+
+  // Min FIXSTR.
+  value = "";
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_FIXSTR_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXSTR.
+  value = std::string((1 << 5) - 1, 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_FIXSTR_MAX};
+  expected.Append((1 << 5) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR8.
+  value = std::string((1 << 5), 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_STR8, (1 << 5)};
+  expected.Append((1 << 5), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max STR8.
+  value = std::string((1 << 8) - 1, 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+  expected.Append((1 << 8) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR16.
+  value = std::string((1 << 8), 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+  expected.Append((1 << 8), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max STR16.
+  value = std::string((1 << 16) - 1, 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+  expected.Append((1 << 16) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min STR32.
+  value = std::string((1 << 16), 'x');
+  Serialize(WrapString(value), &result);
+  expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, vector) {
+  Payload result;
+  Payload expected;
+  std::vector<uint8_t> value;
+
+  // Min FIXARRAY.
+  value = {};
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXARRAY.
+  value = decltype(value)((1 << 4) - 1, 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MAX};
+  expected.Append((1 << 4) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY16.
+  value = decltype(value)((1 << 4), 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+  expected.Append((1 << 4), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max ARRAY16.
+  value = decltype(value)((1 << 16) - 1, 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  expected.Append((1 << 16) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY32.
+  value = decltype(value)((1 << 16), 'x');
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, map) {
+  Payload result;
+  Payload expected;
+  std::map<std::uint32_t, std::uint32_t> value;
+
+  // Min FIXMAP.
+  value = {};
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXMAP.
+  value = MakeMap<decltype(value)>((1 << 4) - 1);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MAX};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min MAP16.
+  value = MakeMap<decltype(value)>((1 << 4));
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max MAP16.
+  value = MakeMap<decltype(value)>((1 << 16) - 1);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min MAP32.
+  value = MakeMap<decltype(value)>((1 << 16));
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, unordered_map) {
+  Payload result;
+  Payload expected;
+  std::unordered_map<std::uint32_t, std::uint32_t> value;
+
+  // Min FIXMAP.
+  value = {};
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXMAP.
+  value = MakeMap<decltype(value)>((1 << 4) - 1);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MAX};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min MAP16.
+  value = MakeMap<decltype(value)>((1 << 4));
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max MAP16.
+  value = MakeMap<decltype(value)>((1 << 16) - 1);
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min MAP32.
+  value = MakeMap<decltype(value)>((1 << 16));
+  Serialize(value, &result);
+  expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+  InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, array) {
+  Payload result;
+  Payload expected;
+
+  // Min FIXARRAY.
+  std::array<std::uint8_t, 0> a0;
+  Serialize(a0, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXARRAY.
+  std::array<std::uint8_t, (1 << 4) - 1> a1;
+  for (auto& element : a1)
+    element = 'x';
+  Serialize(a1, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MAX};
+  expected.Append((1 << 4) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY16.
+  std::array<std::uint8_t, (1 << 4)> a2;
+  for (auto& element : a2)
+    element = 'x';
+  Serialize(a2, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+  expected.Append((1 << 4), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max ARRAY16.
+  std::array<std::uint8_t, (1 << 16) - 1> a3;
+  for (auto& element : a3)
+    element = 'x';
+  Serialize(a3, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  expected.Append((1 << 16) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY32.
+  std::array<std::uint8_t, (1 << 16)> a4;
+  for (auto& element : a4)
+    element = 'x';
+  Serialize(a4, &result);
+  expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, ArrayWrapper) {
+  Payload result;
+  Payload expected;
+  std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> value;
+  ArrayWrapper<std::uint8_t> wrapper;
+
+  // Min FIXARRAY.
+  value = {};
+  Serialize(wrapper, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXARRAY.
+  value = decltype(value)((1 << 4) - 1, 'x');
+  wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+  Serialize(wrapper, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MAX};
+  expected.Append((1 << 4) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY16.
+  value = decltype(value)((1 << 4), 'x');
+  wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+  Serialize(wrapper, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+  expected.Append((1 << 4), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max ARRAY16.
+  value = decltype(value)((1 << 16) - 1, 'x');
+  wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+  Serialize(wrapper, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  expected.Append((1 << 16) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY32.
+  value = decltype(value)((1 << 16), 'x');
+  wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+  Serialize(wrapper, &result);
+  expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, pair) {
+  Payload result;
+  Payload expected;
+
+  auto p1 = std::make_pair(1, 2);
+  Serialize(p1, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  auto p2 = std::make_pair('x', std::string("12345"));
+  Serialize(p2, &result);
+  expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+                                 ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3',
+                                 '4', '5'});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, tuple) {
+  Payload result;
+  Payload expected;
+
+  // Min FIXARRAY.
+  auto t1 = std::make_tuple();
+  Serialize(t1, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MIN};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Max FIXARRAY.
+  auto t2 = GetNTuple<15>('x');
+  Serialize(t2, &result);
+  expected = {ENCODING_TYPE_FIXARRAY_MAX};
+  expected.Append((1 << 4) - 1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY16.
+  auto t3 = GetNTuple<(1 << 4)>('x');
+  Serialize(t3, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+  expected.Append((1 << 4), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+// Template instantiation depth is an issue for these tests. They are commented
+// out to document the expected behavior, even though tuples of this order are
+// not expected in practice.
+#if 0
+  // Max ARRAY16.
+  auto t4 = GetNTuple<(1 << 16)-1>('x');
+  Serialize(t4, &result);
+  expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  expected.Append((1 << 16)-1, 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // Min ARRAY32.
+  auto t5 = GetNTuple<(1 << 16)>('x');
+  Serialize(t5, &result);
+  expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  expected.Append((1 << 16), 'x');
+  EXPECT_EQ(expected, result);
+  result.Clear();
+#endif
+}
+
+// TODO(eieio): More exhaustive testing of type nesting.
+TEST(SerializationTest, NestedTuple) {
+  Payload result;
+  Payload expected;
+
+  auto t1 = std::make_tuple('x', std::make_tuple<int, int>(1, 2));
+  Serialize(t1, &result);
+  expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+                                 ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  auto t2 = std::make_tuple('x', std::make_tuple<int, int>(1, 2),
+                            std::string("0123456789"));
+  Serialize(t2, &result);
+  expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 3, 'x',
+                                 ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2,
+                                 ENCODING_TYPE_FIXSTR | 10, '0', '1', '2', '3',
+                                 '4', '5', '6', '7', '8', '9'});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  auto t3 = std::make_tuple(0.0f, std::uint64_t(10ULL),
+                            std::vector<char>{'a', 'b', 'c'});
+  Serialize(t3, &result);
+  expected = decltype(expected)(
+      {ENCODING_TYPE_FIXARRAY_MIN + 3, ENCODING_TYPE_FLOAT32,
+       kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+       kZeroFloatBytes[3], ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10,
+       ENCODING_TYPE_FIXARRAY_MIN + 3, 'a', 'b', 'c'});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, NestedMap) {
+  Payload result;
+  Payload expected;
+
+  std::map<int, std::pair<std::string, int>> m1 = {{0, {"a", 2}},
+                                                   {1, {"b", 10}}};
+  Serialize(m1, &result);
+  expected = decltype(expected)(
+      {ENCODING_TYPE_FIXMAP_MIN + 2, 0, ENCODING_TYPE_FIXARRAY_MIN + 2,
+       ENCODING_TYPE_FIXSTR_MIN + 1, 'a', 2, 1, ENCODING_TYPE_FIXARRAY_MIN + 2,
+       ENCODING_TYPE_FIXSTR_MIN + 1, 'b', 10});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+}
+
+TEST(SerializationTest, Serializable) {
+  Payload result;
+  Payload expected;
+
+  TestType t1{10, 0.0, "12345", TestType::Foo::kBaz};
+  Serialize(t1, &result);
+  expected = decltype(expected)(
+      {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+       kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+       kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+       '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2});
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  TestTemplateType<LocalHandle> tt{LocalHandle(-1)};
+  Serialize(tt, &result);
+  expected =
+      decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+                          ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+  EXPECT_EQ(expected, result);
+}
+
+TEST(SerializationTest, Variant) {
+  Payload result;
+  Payload expected;
+
+  Variant<int, bool, float> v;
+
+  // Empty variant.
+  Serialize(v, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+              ENCODING_TYPE_NIL};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  v = 10;
+  Serialize(v, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+              ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+              ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  v = true;
+  Serialize(v, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+              ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_TRUE};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  v = false;
+  Serialize(v, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+              ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_FALSE};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  v = 1.0f;
+  Serialize(v, &result);
+  expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+              ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+              ENCODING_TYPE_FLOAT32,
+              kOneFloatBytes[0],
+              kOneFloatBytes[1],
+              kOneFloatBytes[2],
+              kOneFloatBytes[3]};
+  EXPECT_EQ(expected, result);
+  result.Clear();
+
+  // TODO(eieio): Add more serialization tests for Variant.
+}
+
+TEST(DeserializationTest, bool) {
+  Payload buffer;
+  bool result = false;
+  ErrorType error;
+
+  // True.
+  buffer = {ENCODING_TYPE_TRUE};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(1, result);  // Gtest generates warning from bool literals.
+
+  // False.
+  buffer = {ENCODING_TYPE_FALSE};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result);  // Gtest generates warning from bool literals.
+}
+
+TEST(DeserializationTest, uint8_t) {
+  Payload buffer;
+  std::uint8_t result = 0;
+  ErrorType error;
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127U, result);
+
+  // Min UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffU, result);
+
+  // UINT16 out of range.
+  buffer = {ENCODING_TYPE_UINT16};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT16, error.encoding_type());
+
+  // UINT32 out of range.
+  buffer = {ENCODING_TYPE_UINT32};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+  // UINT64 out of range.
+  buffer = {ENCODING_TYPE_UINT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint16_t) {
+  Payload buffer;
+  std::uint16_t result = 0;
+  ErrorType error;
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127U, result);
+
+  // Min UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffU, result);
+
+  // Min UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffU, result);
+
+  // UINT32 out of range.
+  buffer = {ENCODING_TYPE_UINT32};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+  // UINT64 out of range.
+  buffer = {ENCODING_TYPE_UINT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint32_t) {
+  Payload buffer;
+  std::uint32_t result = 0;
+  ErrorType error;
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127U, result);
+
+  // Min UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffU, result);
+
+  // Min UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffU, result);
+
+  // Min UINT32.
+  buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT32.
+  buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffffffU, result);
+
+  // UINT64 out of range.
+  buffer = {ENCODING_TYPE_UINT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint64_t) {
+  Payload buffer;
+  std::uint64_t result = 0;
+  ErrorType error;
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127U, result);
+
+  // Min UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT8.
+  buffer = {ENCODING_TYPE_UINT8, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffU, result);
+
+  // Min UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT16.
+  buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffU, result);
+
+  // Min UINT32.
+  buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT32.
+  buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffffffU, result);
+
+  // Min UINT64.
+  buffer = {
+      ENCODING_TYPE_UINT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0U, result);
+
+  // Max UINT64.
+  buffer = {
+      ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0xffffffffffffffffUL, result);
+}
+
+TEST(DeserializationTest, int8_t) {
+  Payload buffer;
+  std::int8_t result = 0;
+  ErrorType error;
+
+  // Min NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32, result);
+
+  // Max NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-1, result);
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-128, result);
+
+  // Max INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // INT16 out of range.
+  buffer = {ENCODING_TYPE_INT16};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT16, error.encoding_type());
+
+  // INT32 out of range.
+  buffer = {ENCODING_TYPE_INT32};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+  // INT64 out of range.
+  buffer = {ENCODING_TYPE_INT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int16_t) {
+  Payload buffer;
+  std::int16_t result = 0;
+  ErrorType error;
+
+  // Min NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32, result);
+
+  // Max NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-1, result);
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-128, result);
+
+  // Max INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT16.
+  buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32768, result);
+
+  // Max INT16.
+  buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(32767, result);
+
+  // INT32 out of range.
+  buffer = {ENCODING_TYPE_INT32};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+  // INT64 out of range.
+  buffer = {ENCODING_TYPE_INT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int32_t) {
+  Payload buffer;
+  std::int32_t result = 0;
+  ErrorType error;
+
+  // Min NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32, result);
+
+  // Max NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-1, result);
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-128, result);
+
+  // Max INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT16.
+  buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32768, result);
+
+  // Max INT16.
+  buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(32767, result);
+
+  // Min INT32.
+  buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-2147483648, result);
+
+  // Max INT32.
+  buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(2147483647, result);
+
+  // INT64 out of range.
+  buffer = {ENCODING_TYPE_INT64};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int64_t) {
+  Payload buffer;
+  std::int64_t result = 0;
+  ErrorType error;
+
+  // Min NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32, result);
+
+  // Max NEGATIVE FIXINT.
+  buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-1, result);
+
+  // Min FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result);
+
+  // Max FIXINT.
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-128, result);
+
+  // Max INT8.
+  buffer = {ENCODING_TYPE_INT8, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(127, result);
+
+  // Min INT16.
+  buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-32768, result);
+
+  // Max INT16.
+  buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(32767, result);
+
+  // Min INT32.
+  buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-2147483648, result);
+
+  // Max INT32.
+  buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(2147483647, result);
+
+  // Min INT64.
+  buffer = {
+      ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  // Believe it or not, this is actually the correct way to specify the most
+  // negative signed long long.
+  EXPECT_EQ(-9223372036854775807LL - 1, result);
+
+  // Max INT64.
+  buffer = {
+      ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(9223372036854775807LL, result);
+}
+
+TEST(DeserializationTest, float) {
+  Payload buffer;
+  float result;
+  ErrorType error;
+
+  // FLOAT32.
+  buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+            kZeroFloatBytes[2], kZeroFloatBytes[3]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kZeroFloat, result);
+
+  // FLOAT32.
+  buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+            kOneFloatBytes[2], kOneFloatBytes[3]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kOneFloat, result);
+}
+
+TEST(DeserializationTest, double) {
+  Payload buffer;
+  double result;
+  ErrorType error;
+
+  // FLOAT32.
+  buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+            kZeroFloatBytes[2], kZeroFloatBytes[3]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kZeroDouble, result);
+
+  // FLOAT64.
+  buffer = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+            kZeroDoubleBytes[2],   kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+            kZeroDoubleBytes[5],   kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kZeroDouble, result);
+
+  // FLOAT32.
+  buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+            kOneFloatBytes[2], kOneFloatBytes[3]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kOneDouble, result);
+
+  // FLOAT64.
+  buffer = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+            kOneDoubleBytes[2],    kOneDoubleBytes[3], kOneDoubleBytes[4],
+            kOneDoubleBytes[5],    kOneDoubleBytes[6], kOneDoubleBytes[7]};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kOneDouble, result);
+}
+
+TEST(DeserializationTest, Enum) {
+  Payload buffer;
+  enum Foo { kFoo, kBar, kBaz } result;
+  ErrorType error;
+
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(kBar, result);
+}
+
+TEST(DeserializationTest, EnumClass) {
+  Payload buffer;
+  enum Foo { kFoo, kBar, kBaz } result;
+  ErrorType error;
+
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(Foo::kBaz, result);
+}
+
+TEST(DeserializationTest, LocalHandle) {
+  Payload buffer;
+  LocalHandle result1;
+  LocalHandle result2;
+  ErrorType error;
+
+  buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+  error = Deserialize(&result1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result1.Get());
+  result1.Release();  // Don't close fd 0.
+
+  std::tuple<LocalHandle&, LocalHandle&> t1(result1, result2);
+  buffer = decltype(buffer)(
+      {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+       ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+       ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+  error = Deserialize(&t1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(0, result1.Get());
+  EXPECT_EQ(1, result2.Get());
+  result1.Release();  // Don't close fd 0.
+  result2.Release();  // Don't close fd 1.
+
+  buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+            0xff};
+  error = Deserialize(&result1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(-2, result1.Get());
+}
+
+TEST(DeserializationTest, string) {
+  Payload buffer;
+  std::string result = "";
+  ErrorType error;
+
+  // Min FIXSTR.
+  buffer = {ENCODING_TYPE_FIXSTR_MIN};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ("", result);
+
+  // Max FIXSTR.
+  buffer = {ENCODING_TYPE_FIXSTR_MAX};
+  buffer.Append((1 << 5) - 1, 'x');
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::string((1 << 5) - 1, 'x'), result);
+
+  // Min STR8.
+  buffer = {ENCODING_TYPE_STR8, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ("", result);
+
+  // Max STR8.
+  buffer = {ENCODING_TYPE_STR8, 0xff};
+  buffer.Append(0xff, 'x');
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::string(0xff, 'x'), result);
+
+  // Min STR16.
+  buffer = {ENCODING_TYPE_STR16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ("", result);
+
+  // Max STR16.
+  buffer = {ENCODING_TYPE_STR16, 0xff, 0xff};
+  buffer.Append(0xffff, 'x');
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::string(0xffff, 'x'), result);
+
+  // Min STR32.
+  buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ("", result);
+
+  // Test STR32 with max STR16 + 1 bytes. It's not practical to test max
+  // STR32.
+  buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+  buffer.Append(0x10000, 'x');
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::string(0x10000, 'x'), result);
+}
+
+TEST(DeserializationTest, vector) {
+  Payload buffer;
+  std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+      result;
+  Payload expected;
+  ErrorType error;
+
+  // Min FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+  buffer.Append((1 << 4) - 1, 1);
+  error = Deserialize(&result, &buffer);
+  expected = decltype(expected)((1 << 4) - 1, 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  buffer.Append(0xffff, 1);
+  error = Deserialize(&result, &buffer);
+  expected = decltype(expected)(0xffff, 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  buffer.Append(0x10000, 1);
+  error = Deserialize(&result, &buffer);
+  expected = decltype(expected)(0x10000, 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, map) {
+  Payload buffer;
+  std::map<std::uint32_t, std::uint32_t> result;
+  std::map<std::uint32_t, std::uint32_t> expected;
+  ErrorType error;
+
+  // Min FIXMAP.
+  buffer = {ENCODING_TYPE_FIXMAP_MIN};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Size mismatch.
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+  // Max FIXMAP.
+  buffer = {ENCODING_TYPE_FIXMAP_MAX};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error) << std::string(error);
+  EXPECT_EQ(expected, result);
+
+  // Min MAP16.
+  buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max MAP16.
+  buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min MAP32.
+  buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+  buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 16));
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, unordered_map) {
+  Payload buffer;
+  std::unordered_map<std::uint32_t, std::uint32_t> result;
+  std::unordered_map<std::uint32_t, std::uint32_t> expected;
+  ErrorType error;
+
+  // Min FIXMAP.
+  buffer = {ENCODING_TYPE_FIXMAP_MIN};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Size mismatch.
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+  error = Deserialize(&result, &buffer);
+  EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+  // Max FIXMAP.
+  buffer = {ENCODING_TYPE_FIXMAP_MAX};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min MAP16.
+  buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max MAP16.
+  buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min MAP32.
+  buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&result, &buffer);
+  expected = {};
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+  buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+  InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+  error = Deserialize(&result, &buffer);
+  expected = MakeMap<decltype(expected)>((1 << 16));
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, array) {
+  Payload buffer;
+  ErrorType error;
+
+  // Min FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+  std::array<std::uint8_t, 0> a0;
+  error = Deserialize(&a0, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+  // Size mismatch.
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1};
+  error = Deserialize(&a0, &buffer);
+  EXPECT_EQ(ErrorCode::INSUFFICIENT_DESTINATION_SIZE, error);
+
+  // Max FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+  buffer.Append((1 << 4) - 1, 'x');
+  std::array<std::uint8_t, (1 << 4) - 1> a1, expected1;
+  for (auto& element : expected1)
+    element = 'x';
+  error = Deserialize(&a1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected1, a1);
+
+  // Min ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+  error = Deserialize(&a0, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+  // Max ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  buffer.Append((1 << 16) - 1, 'x');
+  std::array<std::uint8_t, (1 << 16) - 1> a3, expected3;
+  for (auto& element : expected3)
+    element = 'x';
+  error = Deserialize(&a3, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected3, a3);
+
+  // Min ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&a0, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+  // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  buffer.Append((1 << 16), 'x');
+  std::array<std::uint8_t, (1 << 16)> a4, expected4;
+  for (auto& element : expected4)
+    element = 'x';
+  error = Deserialize(&a4, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected4, a4);
+}
+
+TEST(DeserializationTest, ArrayWrapper) {
+  Payload buffer;
+  std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+      result;
+  std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+      expected;
+  ErrorType error;
+
+  result.reserve(0x10000);
+  ArrayWrapper<std::uint8_t> wrapper(result.data(), result.capacity());
+
+  // Min FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+  error = Deserialize(&wrapper, &buffer);
+  expected = {};
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max FIXARRAY.
+  buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+  buffer.Append((1 << 4) - 1, 1);
+  error = Deserialize(&wrapper, &buffer);
+  expected = decltype(expected)((1 << 4) - 1, 1);
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+  error = Deserialize(&wrapper, &buffer);
+  expected = {};
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Max ARRAY16.
+  buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+  buffer.Append(0xffff, 1);
+  error = Deserialize(&wrapper, &buffer);
+  expected = decltype(expected)(0xffff, 1);
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // Min ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&wrapper, &buffer);
+  expected = {};
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+
+  // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+  buffer.Append(0x10000, 1);
+  error = Deserialize(&wrapper, &buffer);
+  expected = decltype(expected)(0x10000, 1);
+  result.resize(wrapper.size());
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, pair) {
+  Payload buffer;
+  ErrorType error;
+
+  std::pair<int, int> p1;
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+  error = Deserialize(&p1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::make_pair(1, 2), p1);
+}
+
+TEST(DeserializationTest, tuple) {
+  Payload buffer;
+  ErrorType error;
+
+  // Min FIXARRAY.
+  std::tuple<> t1;
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+  error = Deserialize(&t1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::make_tuple(), t1);  // Superfluous.
+
+  // Max FIXARRAY.
+  auto t2 = GetNTuple<15, int>(0);
+  buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+  buffer.Append((1 << 4) - 1, 1);
+  error = Deserialize(&t2, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ((GetNTuple<15, int>(1)), t2);
+
+  // Min ARRAY16.
+  // Using t1 above.
+  buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+  error = Deserialize(&t1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::make_tuple(), t1);
+
+  // ARRAY16 at Max FIXARRAY + 1
+  auto t3 = GetNTuple<(1 << 4), int>(0);
+  buffer = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+  buffer.Append((1 << 4), 1);
+  error = Deserialize(&t3, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t3);
+
+  // Min ARRAY32.
+  // Using t1 from above.
+  buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+  error = Deserialize(&t1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(std::make_tuple(), t1);
+
+  // ARRAY32 at Max FIXARRAY + 1
+  auto t4 = GetNTuple<(1 << 4), int>(0);
+  buffer = {ENCODING_TYPE_ARRAY32, 0x10, 0x00, 0x00, 0x00};
+  buffer.Append((1 << 4), 1);
+  error = Deserialize(&t4, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t4);
+
+  // Template instantiation depth is an issue for tuples with large numbers of
+  // elements. As these are not expected in practice, the limits of ARRAY16
+  // and ARRAY32 are not tested.
+}
+
+TEST(DeserializationTest, Serializable) {
+  Payload buffer;
+  ErrorType error;
+
+  buffer = decltype(buffer)(
+      {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+       kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+       kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+       '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1});
+  TestType t1;
+  error = Deserialize(&t1, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(TestType(10, 0.f, "12345", TestType::Foo::kBar), t1);
+
+  buffer =
+      decltype(buffer)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+                        ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+  TestTemplateType<LocalHandle> tt;
+  error = Deserialize(&tt, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_EQ(TestTemplateType<LocalHandle>(LocalHandle(-1)), tt);
+}
+
+TEST(DeserializationTest, Variant) {
+  Payload buffer;
+  ErrorType error;
+
+  Variant<int, bool, float> v;
+
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+            ENCODING_TYPE_NIL};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  EXPECT_TRUE(v.empty());
+
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+            ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  ASSERT_TRUE(v.is<int>());
+  EXPECT_EQ(10, std::get<int>(v));
+
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+            ENCODING_TYPE_TRUE};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  ASSERT_TRUE(v.is<bool>());
+  EXPECT_EQ(true, std::get<bool>(v));
+
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+            ENCODING_TYPE_FALSE};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  ASSERT_TRUE(v.is<bool>());
+  EXPECT_EQ(false, std::get<bool>(v));
+
+  buffer = {ENCODING_TYPE_FIXMAP_MIN + 1,
+            ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+            ENCODING_TYPE_FLOAT32,
+            kOneFloatBytes[0],
+            kOneFloatBytes[1],
+            kOneFloatBytes[2],
+            kOneFloatBytes[3]};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::NO_ERROR, error);
+  ASSERT_TRUE(v.is<float>());
+  EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+
+  // TODO(eieio): Add more deserialization tests for Variant.
+}
+
+TEST(DeserializationTest, ErrorType) {
+  Payload buffer;
+  ErrorType error;
+
+  std::uint8_t u8;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&u8, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::uint16_t u16;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&u16, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::uint32_t u32;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&u32, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::uint64_t u64;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&u64, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::int8_t i8;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&i8, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::int16_t i16;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&i16, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::int32_t i32;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&i32, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::int64_t i64;
+  buffer = {ENCODING_TYPE_STR8};
+  error = Deserialize(&i64, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  std::string s;
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+  error = Deserialize(&s, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_STRING, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+  std::vector<std::uint8_t> v;
+  buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_ARRAY, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_STR8};
+  error = Deserialize(&v, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+  EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+  EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 0, 1};
+  std::tuple<int> t;
+  error = Deserialize(&t, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+
+  buffer = {ENCODING_TYPE_FIXARRAY_MIN + 3, 0, 1, 2};
+  std::pair<int, int> p;
+  error = Deserialize(&p, &buffer);
+  EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+}
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
new file mode 100644
index 0000000..fab4770
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,684 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <fcntl.h>
+#include <log/log.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+
+#define TRACE 0
+
+namespace android {
+namespace pdx {
+
+std::shared_ptr<Channel> Channel::GetFromMessageInfo(const MessageInfo& info) {
+  return info.channel ? info.channel->shared_from_this()
+                      : std::shared_ptr<Channel>();
+}
+
+Message::Message() : replied_(true) {}
+
+Message::Message(const MessageInfo& info)
+    : service_{Service::GetFromMessageInfo(info)},
+      channel_{Channel::GetFromMessageInfo(info)},
+      info_{info},
+      replied_{IsImpulse()} {
+  auto svc = service_.lock();
+  if (svc)
+    state_ = svc->endpoint()->AllocateMessageState();
+}
+
+// C++11 specifies the move semantics for shared_ptr but not weak_ptr. This
+// means we have to manually implement the desired move semantics for Message.
+Message::Message(Message&& other) { *this = std::move(other); }
+
+Message& Message::operator=(Message&& other) {
+  Destroy();
+  auto base = reinterpret_cast<std::uint8_t*>(&info_);
+  std::fill(&base[0], &base[sizeof(info_)], 0);
+  replied_ = true;
+  std::swap(service_, other.service_);
+  std::swap(channel_, other.channel_);
+  std::swap(info_, other.info_);
+  std::swap(state_, other.state_);
+  std::swap(replied_, other.replied_);
+  return *this;
+}
+
+Message::~Message() { Destroy(); }
+
+void Message::Destroy() {
+  auto svc = service_.lock();
+  if (svc) {
+    if (!replied_) {
+      ALOGE(
+          "ERROR: Service \"%s\" failed to reply to message: op=%d pid=%d "
+          "cid=%d\n",
+          svc->name_.c_str(), info_.op, info_.pid, info_.cid);
+      svc->DefaultHandleMessage(*this);
+    }
+    svc->endpoint()->FreeMessageState(state_);
+  }
+  state_ = nullptr;
+  service_.reset();
+  channel_.reset();
+}
+
+const std::uint8_t* Message::ImpulseBegin() const {
+  return reinterpret_cast<const std::uint8_t*>(info_.impulse);
+}
+
+const std::uint8_t* Message::ImpulseEnd() const {
+  return ImpulseBegin() + (IsImpulse() ? GetSendLength() : 0);
+}
+
+Status<size_t> Message::ReadVector(const struct iovec* vector,
+                                   size_t vector_length) {
+  PDX_TRACE_NAME("Message::ReadVector");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->ReadMessageData(this, vector, vector_length);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<void> Message::ReadVectorAll(const struct iovec* vector,
+                                    size_t vector_length) {
+  PDX_TRACE_NAME("Message::ReadVectorAll");
+  if (auto svc = service_.lock()) {
+    const auto status =
+        svc->endpoint()->ReadMessageData(this, vector, vector_length);
+    if (!status)
+      return status.error_status();
+    size_t size_to_read = 0;
+    for (size_t i = 0; i < vector_length; i++)
+      size_to_read += vector[i].iov_len;
+    if (status.get() < size_to_read)
+      return ErrorStatus{EIO};
+    return {};
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<size_t> Message::Read(void* buffer, size_t length) {
+  PDX_TRACE_NAME("Message::Read");
+  if (auto svc = service_.lock()) {
+    const struct iovec vector = {buffer, length};
+    return svc->endpoint()->ReadMessageData(this, &vector, 1);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<size_t> Message::WriteVector(const struct iovec* vector,
+                                    size_t vector_length) {
+  PDX_TRACE_NAME("Message::WriteVector");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->WriteMessageData(this, vector, vector_length);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<void> Message::WriteVectorAll(const struct iovec* vector,
+                                     size_t vector_length) {
+  PDX_TRACE_NAME("Message::WriteVector");
+  if (auto svc = service_.lock()) {
+    const auto status =
+        svc->endpoint()->WriteMessageData(this, vector, vector_length);
+    if (!status)
+      return status.error_status();
+    size_t size_to_write = 0;
+    for (size_t i = 0; i < vector_length; i++)
+      size_to_write += vector[i].iov_len;
+    if (status.get() < size_to_write)
+      return ErrorStatus{EIO};
+    return {};
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<size_t> Message::Write(const void* buffer, size_t length) {
+  PDX_TRACE_NAME("Message::Write");
+  if (auto svc = service_.lock()) {
+    const struct iovec vector = {const_cast<void*>(buffer), length};
+    return svc->endpoint()->WriteMessageData(this, &vector, 1);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<FileReference> Message::PushFileHandle(const LocalHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushFileHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<FileReference> Message::PushFileHandle(const BorrowedHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushFileHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<FileReference> Message::PushFileHandle(const RemoteHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushFileHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+    const LocalChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushChannelHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+    const BorrowedChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushChannelHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+    const RemoteChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->PushChannelHandle(this, handle);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) {
+  PDX_TRACE_NAME("Message::GetFileHandle");
+  auto svc = service_.lock();
+  if (!svc)
+    return false;
+
+  if (ref >= 0) {
+    *handle = svc->endpoint()->GetFileHandle(this, ref);
+    if (!handle->IsValid())
+      return false;
+  } else {
+    *handle = LocalHandle{ref};
+  }
+  return true;
+}
+
+bool Message::GetChannelHandle(ChannelReference ref,
+                               LocalChannelHandle* handle) {
+  PDX_TRACE_NAME("Message::GetChannelHandle");
+  auto svc = service_.lock();
+  if (!svc)
+    return false;
+
+  if (ref >= 0) {
+    *handle = svc->endpoint()->GetChannelHandle(this, ref);
+    if (!handle->valid())
+      return false;
+  } else {
+    *handle = LocalChannelHandle{nullptr, ref};
+  }
+  return true;
+}
+
+Status<void> Message::Reply(int return_code) {
+  PDX_TRACE_NAME("Message::Reply");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret = svc->endpoint()->MessageReply(this, return_code);
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::ReplyFileDescriptor(unsigned int fd) {
+  PDX_TRACE_NAME("Message::ReplyFileDescriptor");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret = svc->endpoint()->MessageReplyFd(this, fd);
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::ReplyError(unsigned int error) {
+  PDX_TRACE_NAME("Message::ReplyError");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret =
+        svc->endpoint()->MessageReply(this, -static_cast<int>(error));
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const LocalHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    Status<void> ret;
+
+    if (handle)
+      ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+    else
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const BorrowedHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    Status<void> ret;
+
+    if (handle)
+      ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+    else
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const RemoteHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    Status<void> ret;
+
+    if (handle)
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+    else
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const LocalChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const BorrowedChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::Reply(const RemoteChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret.ok();
+    return ret;
+  } else {
+    return ErrorStatus{EINVAL};
+  }
+}
+
+Status<void> Message::ModifyChannelEvents(int clear_mask, int set_mask) {
+  PDX_TRACE_NAME("Message::ModifyChannelEvents");
+  if (auto svc = service_.lock()) {
+    return svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask,
+                                                set_mask);
+  } else {
+    return ErrorStatus{ESHUTDOWN};
+  }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+    int flags, const std::shared_ptr<Channel>& channel, int* channel_id) {
+  PDX_TRACE_NAME("Message::PushChannel");
+  if (auto svc = service_.lock()) {
+    return svc->PushChannel(this, flags, channel, channel_id);
+  } else {
+    return ErrorStatus(ESHUTDOWN);
+  }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+    Service* service, int flags, const std::shared_ptr<Channel>& channel,
+    int* channel_id) {
+  PDX_TRACE_NAME("Message::PushChannel");
+  return service->PushChannel(this, flags, channel, channel_id);
+}
+
+Status<int> Message::CheckChannel(ChannelReference ref,
+                                  std::shared_ptr<Channel>* channel) const {
+  PDX_TRACE_NAME("Message::CheckChannel");
+  if (auto svc = service_.lock()) {
+    return svc->CheckChannel(this, ref, channel);
+  } else {
+    return ErrorStatus(ESHUTDOWN);
+  }
+}
+
+Status<int> Message::CheckChannel(const Service* service, ChannelReference ref,
+                                  std::shared_ptr<Channel>* channel) const {
+  PDX_TRACE_NAME("Message::CheckChannel");
+  return service->CheckChannel(this, ref, channel);
+}
+
+pid_t Message::GetProcessId() const { return info_.pid; }
+
+pid_t Message::GetThreadId() const { return info_.tid; }
+
+uid_t Message::GetEffectiveUserId() const { return info_.euid; }
+
+gid_t Message::GetEffectiveGroupId() const { return info_.egid; }
+
+int Message::GetChannelId() const { return info_.cid; }
+
+int Message::GetMessageId() const { return info_.mid; }
+
+int Message::GetOp() const { return info_.op; }
+
+int Message::GetFlags() const { return info_.flags; }
+
+size_t Message::GetSendLength() const { return info_.send_len; }
+
+size_t Message::GetReceiveLength() const { return info_.recv_len; }
+
+size_t Message::GetFileDescriptorCount() const { return info_.fd_count; }
+
+std::shared_ptr<Channel> Message::GetChannel() const { return channel_.lock(); }
+
+Status<void> Message::SetChannel(const std::shared_ptr<Channel>& chan) {
+  channel_ = chan;
+  Status<void> status;
+  if (auto svc = service_.lock())
+    status = svc->SetChannel(info_.cid, chan);
+  return status;
+}
+
+std::shared_ptr<Service> Message::GetService() const { return service_.lock(); }
+
+const MessageInfo& Message::GetInfo() const { return info_; }
+
+Service::Service(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+    : name_(name), endpoint_{std::move(endpoint)} {
+  if (!endpoint_)
+    return;
+
+  const auto status = endpoint_->SetService(this);
+  ALOGE_IF(!status, "Failed to set service context because: %s",
+           status.GetErrorMessage().c_str());
+}
+
+Service::~Service() {
+  if (endpoint_) {
+    const auto status = endpoint_->SetService(nullptr);
+    ALOGE_IF(!status, "Failed to clear service context because: %s",
+             status.GetErrorMessage().c_str());
+  }
+}
+
+std::shared_ptr<Service> Service::GetFromMessageInfo(const MessageInfo& info) {
+  return info.service ? info.service->shared_from_this()
+                      : std::shared_ptr<Service>();
+}
+
+bool Service::IsInitialized() const { return endpoint_.get() != nullptr; }
+
+std::shared_ptr<Channel> Service::OnChannelOpen(Message& /*message*/) {
+  return nullptr;
+}
+
+void Service::OnChannelClose(Message& /*message*/,
+                             const std::shared_ptr<Channel>& /*channel*/) {}
+
+Status<void> Service::SetChannel(int channel_id,
+                                 const std::shared_ptr<Channel>& channel) {
+  PDX_TRACE_NAME("Service::SetChannel");
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  const auto status = endpoint_->SetChannel(channel_id, channel.get());
+  if (!status) {
+    ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(),
+          status.GetErrorMessage().c_str());
+
+    // It's possible someone mucked with things behind our back by calling the C
+    // API directly. Since we know the channel id isn't valid, make sure we
+    // don't have it in the channels map.
+    if (status.error() == ENOENT)
+      channels_.erase(channel_id);
+  } else {
+    if (channel != nullptr)
+      channels_[channel_id] = channel;
+    else
+      channels_.erase(channel_id);
+  }
+  return status;
+}
+
+std::shared_ptr<Channel> Service::GetChannel(int channel_id) const {
+  PDX_TRACE_NAME("Service::GetChannel");
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  auto search = channels_.find(channel_id);
+  if (search != channels_.end())
+    return search->second;
+  else
+    return nullptr;
+}
+
+Status<void> Service::CloseChannel(int channel_id) {
+  PDX_TRACE_NAME("Service::CloseChannel");
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  const auto status = endpoint_->CloseChannel(channel_id);
+
+  // Always erase the map entry, in case someone mucked with things behind our
+  // back using the C API directly.
+  channels_.erase(channel_id);
+
+  return status;
+}
+
+Status<void> Service::ModifyChannelEvents(int channel_id, int clear_mask,
+                                          int set_mask) {
+  PDX_TRACE_NAME("Service::ModifyChannelEvents");
+  return endpoint_->ModifyChannelEvents(channel_id, clear_mask, set_mask);
+}
+
+Status<RemoteChannelHandle> Service::PushChannel(
+    Message* message, int flags, const std::shared_ptr<Channel>& channel,
+    int* channel_id) {
+  PDX_TRACE_NAME("Service::PushChannel");
+
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  int channel_id_temp = -1;
+  Status<RemoteChannelHandle> ret =
+      endpoint_->PushChannel(message, flags, channel.get(), &channel_id_temp);
+  ALOGE_IF(!ret.ok(), "%s::PushChannel: Failed to push channel: %s",
+           name_.c_str(), strerror(ret.error()));
+
+  if (channel && channel_id_temp != -1)
+    channels_[channel_id_temp] = channel;
+  if (channel_id)
+    *channel_id = channel_id_temp;
+
+  return ret;
+}
+
+Status<int> Service::CheckChannel(const Message* message, ChannelReference ref,
+                                  std::shared_ptr<Channel>* channel) const {
+  PDX_TRACE_NAME("Service::CheckChannel");
+
+  // Synchronization to maintain consistency between the kernel's channel
+  // context pointer and the userspace channels_ map. Other threads may attempt
+  // to modify the map at the same time, which could cause the channel context
+  // pointer returned by the kernel to be invalid.
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  Channel* channel_context = nullptr;
+  Status<int> ret = endpoint_->CheckChannel(
+      message, ref, channel ? &channel_context : nullptr);
+  if (ret && channel) {
+    if (channel_context)
+      *channel = channel_context->shared_from_this();
+    else
+      *channel = nullptr;
+  }
+
+  return ret;
+}
+
+std::string Service::DumpState(size_t /*max_length*/) { return ""; }
+
+Status<void> Service::HandleMessage(Message& message) {
+  return DefaultHandleMessage(message);
+}
+
+void Service::HandleImpulse(Message& /*impulse*/) {}
+
+Status<void> Service::HandleSystemMessage(Message& message) {
+  const MessageInfo& info = message.GetInfo();
+
+  switch (info.op) {
+    case opcodes::CHANNEL_OPEN: {
+      ALOGD("%s::OnChannelOpen: pid=%d cid=%d\n", name_.c_str(), info.pid,
+            info.cid);
+      message.SetChannel(OnChannelOpen(message));
+      return message.Reply(0);
+    }
+
+    case opcodes::CHANNEL_CLOSE: {
+      ALOGD("%s::OnChannelClose: pid=%d cid=%d\n", name_.c_str(), info.pid,
+            info.cid);
+      OnChannelClose(message, Channel::GetFromMessageInfo(info));
+      message.SetChannel(nullptr);
+      return message.Reply(0);
+    }
+
+    case opcodes::REPORT_SYSPROP_CHANGE:
+      ALOGD("%s:REPORT_SYSPROP_CHANGE: pid=%d cid=%d\n", name_.c_str(),
+            info.pid, info.cid);
+      OnSysPropChange();
+      android::report_sysprop_change();
+      return message.Reply(0);
+
+    case opcodes::DUMP_STATE: {
+      ALOGD("%s:DUMP_STATE: pid=%d cid=%d\n", name_.c_str(), info.pid,
+            info.cid);
+      auto response = DumpState(message.GetReceiveLength());
+      const size_t response_size = response.size() < message.GetReceiveLength()
+                                       ? response.size()
+                                       : message.GetReceiveLength();
+      const Status<size_t> status =
+          message.Write(response.data(), response_size);
+      if (status && status.get() < response_size)
+        return message.ReplyError(EIO);
+      else
+        return message.Reply(status);
+    }
+
+    default:
+      return ErrorStatus{EOPNOTSUPP};
+  }
+}
+
+Status<void> Service::DefaultHandleMessage(Message& message) {
+  const MessageInfo& info = message.GetInfo();
+
+  ALOGD_IF(TRACE, "Service::DefaultHandleMessage: pid=%d cid=%d op=%d\n",
+           info.pid, info.cid, info.op);
+
+  switch (info.op) {
+    case opcodes::CHANNEL_OPEN:
+    case opcodes::CHANNEL_CLOSE:
+    case opcodes::REPORT_SYSPROP_CHANGE:
+    case opcodes::DUMP_STATE:
+      return HandleSystemMessage(message);
+
+    default:
+      return message.ReplyError(EOPNOTSUPP);
+  }
+}
+
+void Service::OnSysPropChange() {}
+
+Status<void> Service::ReceiveAndDispatch() {
+  Message message;
+  const auto status = endpoint_->MessageReceive(&message);
+  if (!status) {
+    ALOGE("Failed to receive message: %s\n", status.GetErrorMessage().c_str());
+    return status;
+  }
+
+  std::shared_ptr<Service> service = message.GetService();
+
+  if (!service) {
+    ALOGE("Service::ReceiveAndDispatch: service context is NULL!!!\n");
+    // Don't block the sender indefinitely in this error case.
+    endpoint_->MessageReply(&message, -EINVAL);
+    return ErrorStatus{EINVAL};
+  }
+
+  if (message.IsImpulse()) {
+    service->HandleImpulse(message);
+    return {};
+  } else if (service->HandleSystemMessage(message)) {
+    return {};
+  } else {
+    return service->HandleMessage(message);
+  }
+}
+
+Status<void> Service::Cancel() { return endpoint_->Cancel(); }
+
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp
new file mode 100644
index 0000000..c7412b7
--- /dev/null
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -0,0 +1,807 @@
+#include <pdx/service.h>
+
+#include <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <pdx/mock_service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::MockEndpoint;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Service;
+using android::pdx::Status;
+
+using testing::A;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Matcher;
+using testing::Ref;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::WithArg;
+using testing::WithoutArgs;
+using testing::_;
+
+namespace {
+
+// Helper functions to construct fake void pointers for tests.
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+
+// Helper matchers for working with iovec structures in tests.
+// Simple matcher for one element iovec array:
+// EXPECT_CALL(mock, method(IoVecMatcher(ptr, size)));
+MATCHER_P2(IoVecMatcher, ptr, size, "") {
+  return arg->iov_base == ptr && arg->iov_len == size;
+}
+
+// Matcher for an array of iovecs:
+// EXPECT_CALL(mock,
+//             method(IoVecMatcher(IoVecArray{{ptr1, size1}, {ptr2, size2}})));
+using IoVecArray = std::vector<iovec>;
+MATCHER_P(IoVecMatcher, iovec_array, "") {
+  for (const iovec& item : iovec_array) {
+    if (arg->iov_base != item.iov_base || arg->iov_len != item.iov_len)
+      return false;
+    arg++;
+  }
+  return true;
+}
+
+using IoVecData = std::vector<std::string>;
+MATCHER_P(IoVecDataMatcher, iovec_data, "") {
+  for (const std::string& item : iovec_data) {
+    std::string data{reinterpret_cast<const char*>(arg->iov_base),
+                     arg->iov_len};
+    if (data != item)
+      return false;
+    arg++;
+  }
+  return true;
+}
+
+MATCHER_P(FileHandleMatcher, value, "") { return arg.Get() == value; }
+MATCHER_P(ChannelHandleMatcher, value, "") { return arg.value() == value; }
+
+enum : int {
+  kTestPid = 1,
+  kTestTid,
+  kTestCid,
+  kTestMid,
+  kTestEuid,
+  kTestEgid,
+  kTestOp,
+};
+
+class MockService : public Service {
+ public:
+  using Service::Service;
+  MOCK_METHOD1(OnChannelOpen, std::shared_ptr<Channel>(Message& message));
+  MOCK_METHOD2(OnChannelClose,
+               void(Message& message, const std::shared_ptr<Channel>& channel));
+  MOCK_METHOD1(HandleMessage, Status<void>(Message& message));
+  MOCK_METHOD1(HandleImpulse, void(Message& impulse));
+  MOCK_METHOD0(OnSysPropChange, void());
+  MOCK_METHOD1(DumpState, std::string(size_t max_length));
+};
+
+class ServiceTest : public testing::Test {
+ public:
+  ServiceTest() {
+    auto endpoint = std::make_unique<testing::StrictMock<MockEndpoint>>();
+    EXPECT_CALL(*endpoint, SetService(_))
+        .Times(2)
+        .WillRepeatedly(Return(Status<void>{}));
+    service_ = std::make_shared<MockService>("MockSvc", std::move(endpoint));
+  }
+
+  MockEndpoint* endpoint() {
+    return static_cast<MockEndpoint*>(service_->endpoint());
+  }
+
+  void SetupMessageInfo(MessageInfo* info, int32_t op, bool impulse = false) {
+    info->pid = kTestPid;
+    info->tid = kTestTid;
+    info->cid = kTestCid;
+    info->mid = impulse ? Message::IMPULSE_MESSAGE_ID : kTestMid;
+    info->euid = kTestEuid;
+    info->egid = kTestEgid;
+    info->op = op;
+    info->flags = 0;
+    info->service = service_.get();
+    info->channel = nullptr;
+    info->send_len = 0;
+    info->recv_len = 0;
+    info->fd_count = 0;
+    memset(info->impulse, 0, sizeof(info->impulse));
+  }
+
+  void SetupMessageInfoAndDefaultExpectations(MessageInfo* info, int32_t op,
+                                              bool impulse = false) {
+    SetupMessageInfo(info, op, impulse);
+    EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+    EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+  }
+
+  void ExpectDefaultHandleMessage() {
+    EXPECT_CALL(*endpoint(), MessageReply(_, -EOPNOTSUPP))
+        .WillOnce(Return(Status<void>{}));
+  }
+
+  std::shared_ptr<MockService> service_;
+  void* kState = IntToPtr(123456);
+};
+
+class ServiceMessageTest : public ServiceTest {
+ public:
+  ServiceMessageTest() {
+    MessageInfo info;
+    SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+    message_ = std::make_unique<Message>(info);
+  }
+
+  std::unique_ptr<Message> message_;
+};
+
+}  // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Service class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceTest, IsInitialized) {
+  EXPECT_TRUE(service_->IsInitialized());
+  service_ = std::make_shared<MockService>("MockSvc2", nullptr);
+  EXPECT_FALSE(service_->IsInitialized());
+}
+
+TEST_F(ServiceTest, ConstructMessage) {
+  MessageInfo info;
+  SetupMessageInfo(&info, kTestOp);
+  auto test_channel = std::make_shared<Channel>();
+  info.channel = test_channel.get();
+  EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+
+  Message message{info};
+
+  EXPECT_FALSE(message.IsImpulse());
+  EXPECT_EQ(kTestPid, message.GetProcessId());
+  EXPECT_EQ(kTestTid, message.GetThreadId());
+  EXPECT_EQ(kTestCid, message.GetChannelId());
+  EXPECT_EQ(kTestMid, message.GetMessageId());
+  EXPECT_EQ(kTestEuid, message.GetEffectiveUserId());
+  EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId());
+  EXPECT_EQ(kTestOp, message.GetOp());
+  EXPECT_EQ(service_, message.GetService());
+  EXPECT_EQ(test_channel, message.GetChannel());
+  EXPECT_FALSE(message.replied());
+  EXPECT_FALSE(message.IsChannelExpired());
+  EXPECT_FALSE(message.IsServiceExpired());
+  EXPECT_EQ(kState, message.GetState());
+
+  ExpectDefaultHandleMessage();
+  EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+}
+
+TEST_F(ServiceTest, ConstructImpulseMessage) {
+  MessageInfo info;
+  SetupMessageInfo(&info, kTestOp, true);
+  auto test_channel = std::make_shared<Channel>();
+  info.channel = test_channel.get();
+  EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+  Message message{info};
+
+  EXPECT_TRUE(message.IsImpulse());
+  EXPECT_EQ(kTestOp, message.GetOp());
+  EXPECT_EQ(service_, message.GetService());
+  EXPECT_EQ(test_channel, message.GetChannel());
+  EXPECT_TRUE(message.replied());
+  EXPECT_FALSE(message.IsChannelExpired());
+  EXPECT_FALSE(message.IsServiceExpired());
+
+  // DefaultHandleMessage should not be called here.
+  EXPECT_CALL(*endpoint(), FreeMessageState(nullptr));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelOpen) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info,
+                                         android::pdx::opcodes::CHANNEL_OPEN);
+  Message message{info};
+
+  auto channel = std::make_shared<Channel>();
+  EXPECT_CALL(*service_, OnChannelOpen(Ref(message))).WillOnce(Return(channel));
+  EXPECT_CALL(*endpoint(), SetChannel(kTestCid, channel.get()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelClose) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info,
+                                         android::pdx::opcodes::CHANNEL_CLOSE);
+  auto channel = std::make_shared<Channel>();
+  info.channel = channel.get();
+  Message message{info};
+
+  EXPECT_CALL(*service_, OnChannelClose(Ref(message), channel));
+  EXPECT_CALL(*endpoint(), SetChannel(kTestCid, nullptr))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnSysPropChange) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(
+      &info, android::pdx::opcodes::REPORT_SYSPROP_CHANGE);
+  Message message{info};
+
+  EXPECT_CALL(*service_, OnSysPropChange());
+  EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpState) {
+  const size_t kRecvBufSize = 1000;
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info,
+                                         android::pdx::opcodes::DUMP_STATE);
+  info.recv_len = kRecvBufSize;
+  Message message{info};
+
+  const std::string kReply = "foo";
+  EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+  EXPECT_CALL(
+      *endpoint(),
+      WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+      .WillOnce(Return(kReply.size()));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, kReply.size()))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateTooLarge) {
+  const size_t kRecvBufSize = 3;
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info,
+                                         android::pdx::opcodes::DUMP_STATE);
+  info.recv_len = kRecvBufSize;
+  Message message{info};
+
+  const std::string kReply = "0123456789";
+  const std::string kActualReply = kReply.substr(0, kRecvBufSize);
+  EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+  EXPECT_CALL(
+      *endpoint(),
+      WriteMessageData(&message, IoVecDataMatcher(IoVecData{kActualReply}), 1))
+      .WillOnce(Return(kActualReply.size()));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, kActualReply.size()))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateFail) {
+  const size_t kRecvBufSize = 1000;
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info,
+                                         android::pdx::opcodes::DUMP_STATE);
+  info.recv_len = kRecvBufSize;
+  Message message{info};
+
+  const std::string kReply = "foo";
+  EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+  EXPECT_CALL(
+      *endpoint(),
+      WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+      .WillOnce(Return(1));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, -EIO))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageCustom) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+  Message message{info};
+
+  EXPECT_CALL(*endpoint(), MessageReply(&message, -EOPNOTSUPP))
+      .WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, ReplyMessageWithoutService) {
+  MessageInfo info;
+  SetupMessageInfo(&info, kTestOp);
+  EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+  Message message{info};
+
+  EXPECT_FALSE(message.IsServiceExpired());
+  service_.reset();
+  EXPECT_TRUE(message.IsServiceExpired());
+
+  EXPECT_EQ(EINVAL, message.Reply(12).error());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchMessage) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+  ExpectDefaultHandleMessage();
+
+  auto on_receive = [&info](Message* message) -> Status<void> {
+    *message = Message{info};
+    return {};
+  };
+  EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+  EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(Status<void>{}));
+
+  EXPECT_TRUE(service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchImpulse) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true);
+
+  auto on_receive = [&info](Message* message) -> Status<void> {
+    *message = Message{info};
+    return {};
+  };
+  EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+  EXPECT_CALL(*service_, HandleImpulse(_));
+
+  EXPECT_TRUE(service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, Cancel) {
+  EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(service_->Cancel());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Message class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceMessageTest, Reply) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_FALSE(message_->replied());
+  EXPECT_TRUE(message_->Reply(12));
+  EXPECT_TRUE(message_->replied());
+
+  EXPECT_EQ(EINVAL, message_->Reply(12).error());  // Already replied.
+}
+
+TEST_F(ServiceMessageTest, ReplyFail) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(EIO, message_->Reply(12).error());
+
+  ExpectDefaultHandleMessage();
+}
+
+TEST_F(ServiceMessageTest, ReplyError) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->ReplyError(12));
+}
+
+TEST_F(ServiceMessageTest, ReplyFileDescriptor) {
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->ReplyFileDescriptor(5));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandle) {
+  const int kFakeFd = 12345;
+  LocalHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+  handle.Release();  // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandleError) {
+  LocalHandle handle{-EINVAL};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EINVAL))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) {
+  const int kFakeFd = 12345;
+  BorrowedHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) {
+  BorrowedHandle handle{-EACCES};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) {
+  RemoteHandle handle{123};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) {
+  RemoteHandle handle{-EIO};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) {
+  LocalChannelHandle handle{nullptr, 12345};
+  EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+                               message_.get(), A<const LocalChannelHandle&>()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) {
+  BorrowedChannelHandle handle{12345};
+  EXPECT_CALL(*endpoint(),
+              MessageReplyChannelHandle(message_.get(),
+                                        A<const BorrowedChannelHandle&>()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) {
+  RemoteChannelHandle handle{12345};
+  EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+                               message_.get(), A<const RemoteChannelHandle&>()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusInt) {
+  Status<int> status{123};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusError) {
+  Status<int> status{ErrorStatus{EIO}};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error()))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, Read) {
+  ExpectDefaultHandleMessage();
+  void* const kDataBuffer = IntToPtr(12345);
+  const size_t kDataSize = 100;
+  EXPECT_CALL(
+      *endpoint(),
+      ReadMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+      .WillOnce(Return(50))
+      .WillOnce(Return(ErrorStatus{EACCES}));
+  EXPECT_EQ(50u, message_->Read(kDataBuffer, kDataSize).get());
+  EXPECT_EQ(EACCES, message_->Read(kDataBuffer, kDataSize).error());
+}
+
+TEST_F(ServiceMessageTest, ReadVector) {
+  ExpectDefaultHandleMessage();
+  char buffer1[10];
+  char buffer2[20];
+  iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+  EXPECT_CALL(*endpoint(),
+              ReadMessageData(
+                  message_.get(),
+                  IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+      .WillOnce(Return(30))
+      .WillOnce(Return(15))
+      .WillOnce(Return(ErrorStatus{EBADF}));
+  EXPECT_EQ(30u, message_->ReadVector(vec, 2).get());
+  EXPECT_EQ(15u, message_->ReadVector(vec).get());
+  EXPECT_EQ(EBADF, message_->ReadVector(vec).error());
+}
+
+TEST_F(ServiceMessageTest, Write) {
+  ExpectDefaultHandleMessage();
+  void* const kDataBuffer = IntToPtr(12345);
+  const size_t kDataSize = 100;
+  EXPECT_CALL(
+      *endpoint(),
+      WriteMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+      .WillOnce(Return(50))
+      .WillOnce(Return(ErrorStatus{EBADMSG}));
+  EXPECT_EQ(50u, message_->Write(kDataBuffer, kDataSize).get());
+  EXPECT_EQ(EBADMSG, message_->Write(kDataBuffer, kDataSize).error());
+}
+
+TEST_F(ServiceMessageTest, WriteVector) {
+  ExpectDefaultHandleMessage();
+  char buffer1[10];
+  char buffer2[20];
+  iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+  EXPECT_CALL(*endpoint(),
+              WriteMessageData(
+                  message_.get(),
+                  IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+      .WillOnce(Return(30))
+      .WillOnce(Return(15))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(30u, message_->WriteVector(vec, 2).get());
+  EXPECT_EQ(15u, message_->WriteVector(vec).get());
+  EXPECT_EQ(EIO, message_->WriteVector(vec, 2).error());
+}
+
+TEST_F(ServiceMessageTest, PushLocalFileHandle) {
+  ExpectDefaultHandleMessage();
+  const int kFakeFd = 12345;
+  LocalHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(),
+              PushFileHandle(message_.get(), Matcher<const LocalHandle&>(
+                                                 FileHandleMatcher(kFakeFd))))
+      .WillOnce(Return(12))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(12, message_->PushFileHandle(handle).get());
+  EXPECT_EQ(EIO, message_->PushFileHandle(handle).error());
+  handle.Release();  // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedFileHandle) {
+  ExpectDefaultHandleMessage();
+  const int kFakeFd = 12345;
+  BorrowedHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(),
+              PushFileHandle(message_.get(), Matcher<const BorrowedHandle&>(
+                                                 FileHandleMatcher(kFakeFd))))
+      .WillOnce(Return(13))
+      .WillOnce(Return(ErrorStatus{EACCES}));
+  EXPECT_EQ(13, message_->PushFileHandle(handle).get());
+  EXPECT_EQ(EACCES, message_->PushFileHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushRemoteFileHandle) {
+  ExpectDefaultHandleMessage();
+  const int kFakeFd = 12345;
+  RemoteHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(),
+              PushFileHandle(message_.get(), Matcher<const RemoteHandle&>(
+                                                 FileHandleMatcher(kFakeFd))))
+      .WillOnce(Return(kFakeFd))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle).get());
+  EXPECT_EQ(EIO, message_->PushFileHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushLocalChannelHandle) {
+  ExpectDefaultHandleMessage();
+  int32_t kValue = 12345;
+  LocalChannelHandle handle{nullptr, kValue};
+  EXPECT_CALL(*endpoint(), PushChannelHandle(message_.get(),
+                                             Matcher<const LocalChannelHandle&>(
+                                                 ChannelHandleMatcher(kValue))))
+      .WillOnce(Return(7))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(7, message_->PushChannelHandle(handle).get());
+  EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedChannelHandle) {
+  ExpectDefaultHandleMessage();
+  int32_t kValue = 12345;
+  BorrowedChannelHandle handle{kValue};
+  EXPECT_CALL(
+      *endpoint(),
+      PushChannelHandle(message_.get(), Matcher<const BorrowedChannelHandle&>(
+                                            ChannelHandleMatcher(kValue))))
+      .WillOnce(Return(8))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(8, message_->PushChannelHandle(handle).get());
+  EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushRemoteChannelHandle) {
+  ExpectDefaultHandleMessage();
+  int32_t kValue = 12345;
+  RemoteChannelHandle handle{kValue};
+  EXPECT_CALL(
+      *endpoint(),
+      PushChannelHandle(message_.get(), Matcher<const RemoteChannelHandle&>(
+                                            ChannelHandleMatcher(kValue))))
+      .WillOnce(Return(kValue))
+      .WillOnce(Return(ErrorStatus{EIO}));
+  EXPECT_EQ(kValue, message_->PushChannelHandle(handle).get());
+  EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandle) {
+  ExpectDefaultHandleMessage();
+  auto make_file_handle = [](FileReference ref) { return LocalHandle{ref}; };
+  EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+      .WillOnce(WithArg<1>(Invoke(make_file_handle)));
+  LocalHandle handle;
+  FileReference kRef = 12345;
+  EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+  EXPECT_EQ(kRef, handle.Get());
+  handle.Release();  // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleInvalid) {
+  ExpectDefaultHandleMessage();
+  LocalHandle handle;
+  FileReference kRef = -12;
+  EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+  EXPECT_EQ(kRef, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleError) {
+  ExpectDefaultHandleMessage();
+  EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+      .WillOnce(WithoutArgs(Invoke([] { return LocalHandle{-EIO}; })));
+  LocalHandle handle;
+  FileReference kRef = 12345;
+  EXPECT_FALSE(message_->GetFileHandle(kRef, &handle));
+  EXPECT_EQ(-EIO, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandle) {
+  ExpectDefaultHandleMessage();
+  auto make_channel_handle = [](ChannelReference ref) {
+    return LocalChannelHandle{nullptr, ref};
+  };
+  EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+      .WillOnce(WithArg<1>(Invoke(make_channel_handle)));
+  LocalChannelHandle handle;
+  ChannelReference kRef = 12345;
+  EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+  EXPECT_EQ(kRef, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleInvalid) {
+  ExpectDefaultHandleMessage();
+  LocalChannelHandle handle;
+  ChannelReference kRef = -12;
+  EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+  EXPECT_EQ(-12, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleError) {
+  ExpectDefaultHandleMessage();
+  EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+      .WillOnce(WithoutArgs(Invoke([] {
+        return LocalChannelHandle{nullptr, -EIO};
+      })));
+  LocalChannelHandle handle;
+  ChannelReference kRef = 12345;
+  EXPECT_FALSE(message_->GetChannelHandle(kRef, &handle));
+  EXPECT_EQ(-EIO, handle.value());
+}
+
+TEST_F(ServiceMessageTest, ModifyChannelEvents) {
+  ExpectDefaultHandleMessage();
+  int kClearMask = 1;
+  int kSetMask = 2;
+  EXPECT_CALL(*endpoint(), ModifyChannelEvents(kTestCid, kClearMask, kSetMask))
+      .WillOnce(Return(Status<void>{}));
+  EXPECT_TRUE(message_->ModifyChannelEvents(kClearMask, kSetMask));
+}
+
+TEST_F(ServiceMessageTest, PushChannelSameService) {
+  ExpectDefaultHandleMessage();
+  int kFlags = 123;
+  int32_t kValue = 12;
+  EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+      .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+                      Return(ByMove(RemoteChannelHandle{kValue}))));
+  int channel_id = -1;
+  auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+  ASSERT_TRUE(status);
+  EXPECT_EQ(kValue, status.get().value());
+  EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, PushChannelFailure) {
+  ExpectDefaultHandleMessage();
+  int kFlags = 123;
+  EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+      .WillOnce(Return(ByMove(ErrorStatus{EIO})));
+  int channel_id = -1;
+  auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ServiceMessageTest, PushChannelDifferentService) {
+  ExpectDefaultHandleMessage();
+  auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+  EXPECT_CALL(*endpoint2, SetService(_))
+      .Times(2)
+      .WillRepeatedly(Return(Status<void>{}));
+  auto service2 =
+      std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+  int kFlags = 123;
+  int32_t kValue = 12;
+  EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+              PushChannel(message_.get(), kFlags, nullptr, _))
+      .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+                      Return(ByMove(RemoteChannelHandle{kValue}))));
+  int channel_id = -1;
+  auto status =
+      message_->PushChannel(service2.get(), kFlags, nullptr, &channel_id);
+  ASSERT_TRUE(status);
+  EXPECT_EQ(kValue, status.get().value());
+  EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelSameService) {
+  ExpectDefaultHandleMessage();
+
+  auto test_channel = std::make_shared<Channel>();
+  ChannelReference kRef = 123;
+  EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+      .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+  std::shared_ptr<Channel> channel;
+  auto status = message_->CheckChannel(kRef, &channel);
+  ASSERT_TRUE(status);
+  EXPECT_EQ(kTestCid, status.get());
+  EXPECT_EQ(test_channel, channel);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelFailure) {
+  ExpectDefaultHandleMessage();
+  ChannelReference kRef = 123;
+  EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+      .WillOnce(Return(ByMove(ErrorStatus{EOPNOTSUPP})));
+  std::shared_ptr<Channel> channel;
+  auto status = message_->CheckChannel(kRef, &channel);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EOPNOTSUPP, status.error());
+}
+
+TEST_F(ServiceMessageTest, CheckChannelDifferentService) {
+  ExpectDefaultHandleMessage();
+  auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+  EXPECT_CALL(*endpoint2, SetService(_))
+      .Times(2)
+      .WillRepeatedly(Return(Status<void>{}));
+  auto service2 =
+      std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+  auto test_channel = std::make_shared<Channel>();
+  ChannelReference kRef = 123;
+  EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+              CheckChannel(message_.get(), kRef, _))
+      .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+  std::shared_ptr<Channel> channel;
+  auto status = message_->CheckChannel(service2.get(), kRef, &channel);
+  ASSERT_TRUE(status);
+  EXPECT_EQ(kTestCid, status.get());
+  EXPECT_EQ(test_channel, channel);
+}
diff --git a/libs/vr/libpdx/status.cpp b/libs/vr/libpdx/status.cpp
new file mode 100644
index 0000000..c275daf
--- /dev/null
+++ b/libs/vr/libpdx/status.cpp
@@ -0,0 +1,15 @@
+#include "pdx/status.h"
+
+#include <pdx/rpc/serialization.h>
+#include <string.h>
+
+namespace android {
+namespace pdx {
+
+std::string ErrorStatus::ErrorToString(int error_code) {
+  char message[1024] = {};
+  return strerror_r(error_code, message, sizeof(message));
+}
+
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx/status_tests.cpp b/libs/vr/libpdx/status_tests.cpp
new file mode 100644
index 0000000..d4e697c
--- /dev/null
+++ b/libs/vr/libpdx/status_tests.cpp
@@ -0,0 +1,125 @@
+#include <pdx/status.h>
+
+#include <gtest/gtest.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+TEST(Status, DefaultInit) {
+  Status<int> status;
+  EXPECT_FALSE(status.ok());
+  EXPECT_TRUE(status.empty());
+  EXPECT_EQ(0, status.get());
+  EXPECT_EQ(0, status.error());
+}
+
+TEST(Status, InitalizeSuccess) {
+  Status<int> status_int{0};
+  EXPECT_FALSE(status_int.empty());
+  EXPECT_TRUE(status_int.ok());
+  EXPECT_EQ(0, status_int.get());
+  status_int = Status<int>(3);
+  EXPECT_FALSE(status_int.empty());
+  EXPECT_TRUE(status_int.ok());
+  EXPECT_EQ(3, status_int.get());
+  status_int = Status<int>(-3);
+  EXPECT_FALSE(status_int.empty());
+  EXPECT_TRUE(status_int.ok());
+  EXPECT_EQ(-3, status_int.get());
+
+  Status<std::string> status_str{"foo"};
+  EXPECT_FALSE(status_str.empty());
+  EXPECT_TRUE(status_str.ok());
+  EXPECT_EQ("foo", status_str.get());
+}
+
+TEST(Status, InitalizeError) {
+  Status<int> status_int = ErrorStatus(12);
+  EXPECT_FALSE(status_int.empty());
+  EXPECT_FALSE(status_int.ok());
+  EXPECT_EQ(0, status_int.get());
+  EXPECT_EQ(12, status_int.error());
+
+  Status<std::string> status_str = ErrorStatus(EIO);
+  EXPECT_FALSE(status_str.empty());
+  EXPECT_FALSE(status_str.ok());
+  EXPECT_EQ(EIO, status_str.error());
+}
+
+TEST(Status, ErrorMessage) {
+  Status<int> status = ErrorStatus(EIO);
+  EXPECT_EQ(status.GetErrorMessage(), strerror(EIO));
+
+  status = ErrorStatus(EINVAL);
+  EXPECT_EQ(status.GetErrorMessage(), strerror(EINVAL));
+}
+
+TEST(Status, Copy) {
+  Status<int> status1;
+  Status<int> status2;
+
+  status1 = Status<int>{12};
+  status2 = ErrorStatus(13);
+  EXPECT_FALSE(status1.empty());
+  EXPECT_FALSE(status2.empty());
+  EXPECT_TRUE(status1.ok());
+  EXPECT_FALSE(status2.ok());
+  EXPECT_EQ(12, status1.get());
+  EXPECT_EQ(0, status1.error());
+  EXPECT_EQ(0, status2.get());
+  EXPECT_EQ(13, status2.error());
+
+  status1 = status2;
+  EXPECT_FALSE(status1.empty());
+  EXPECT_FALSE(status2.empty());
+  EXPECT_FALSE(status1.ok());
+  EXPECT_FALSE(status2.ok());
+  EXPECT_EQ(0, status1.get());
+  EXPECT_EQ(13, status1.error());
+  EXPECT_EQ(0, status2.get());
+  EXPECT_EQ(13, status2.error());
+}
+
+TEST(Status, Move) {
+  Status<std::unique_ptr<int>> status1;
+  Status<std::unique_ptr<int>> status2;
+
+  status1 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{11}}};
+  status2 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{12}}};
+  EXPECT_FALSE(status1.empty());
+  EXPECT_FALSE(status2.empty());
+  EXPECT_TRUE(status1.ok());
+  EXPECT_TRUE(status2.ok());
+  EXPECT_EQ(11, *status1.get());
+  EXPECT_EQ(12, *status2.get());
+
+  Status<std::unique_ptr<int>> status3 = std::move(status2);
+  EXPECT_FALSE(status1.empty());
+  EXPECT_TRUE(status2.empty());
+  EXPECT_FALSE(status3.empty());
+  EXPECT_TRUE(status1.ok());
+  EXPECT_FALSE(status2.ok());
+  EXPECT_TRUE(status3.ok());
+  EXPECT_EQ(11, *status1.get());
+  EXPECT_EQ(nullptr, status2.get());
+  EXPECT_EQ(12, *status3.get());
+
+  std::swap(status1, status3);
+  EXPECT_EQ(12, *status1.get());
+  EXPECT_EQ(11, *status3.get());
+
+  status3 = std::move(status1);
+  EXPECT_TRUE(status1.empty());
+  EXPECT_EQ(12, *status3.get());
+}
+
+TEST(Status, Take) {
+  Status<std::unique_ptr<int>> status{std::unique_ptr<int>{new int{123}}};
+  EXPECT_FALSE(status.empty());
+  EXPECT_NE(nullptr, status.get());
+
+  auto data = status.take();
+  EXPECT_TRUE(status.empty());
+  EXPECT_EQ(nullptr, status.get());
+  EXPECT_EQ(123, *data);
+}
diff --git a/libs/vr/libpdx/thread_local_buffer_tests.cpp b/libs/vr/libpdx/thread_local_buffer_tests.cpp
new file mode 100644
index 0000000..6cdaf10
--- /dev/null
+++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp
@@ -0,0 +1,117 @@
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/message_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+class ThreadLocalBufferTest {
+ public:
+  // Returns the unique address of the thread-local buffer. Used to test the
+  // correct behavior of the type-based thread local storage slot mapping
+  // mechanism.
+  template <typename Slot>
+  static std::uintptr_t GetSlotAddress() {
+    return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_);
+  }
+
+  // Returns the raw value of the thread local buffer. Used to test the behavior
+  // of backing buffer initialization.
+  template <typename Slot>
+  static std::uintptr_t GetSlotValue() {
+    return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_);
+  }
+};
+
+}  // namespace rpc
+}  // namespace pdx
+}  // namespace android
+
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct TypeTagA;
+struct TypeTagB;
+
+constexpr std::size_t kSendBufferIndex = 0;
+constexpr std::size_t kReceiveBufferIndex = 1;
+
+using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>;
+using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>;
+using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>;
+using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>;
+
+}  // anonymous namespace
+
+// Tests that index and type-based thread-local slot addressing works by
+// checking that the slot address is the same when the same index/type
+// combination is used and different when different combinations are used.
+TEST(ThreadLocalBufferTest, TypeSlots) {
+  auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+  auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+  auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+  auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+  EXPECT_NE(id1, id2);
+  EXPECT_NE(id3, id4);
+  EXPECT_NE(id1, id3);
+  EXPECT_NE(id2, id4);
+
+  auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+  auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+  auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+  auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+  EXPECT_EQ(id1, id1_alias);
+  EXPECT_EQ(id2, id2_alias);
+  EXPECT_EQ(id3, id3_alias);
+  EXPECT_EQ(id4, id4_alias);
+}
+
+// Tests that different threads get different buffers for the same slot address.
+TEST(ThreadLocalBufferTest, ThreadSlots) {
+  auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+  std::uintptr_t id2 = 0U;
+
+  std::thread thread([&id2]() mutable {
+    id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+  });
+  thread.join();
+
+  EXPECT_NE(0U, id1);
+  EXPECT_NE(0U, id2);
+  EXPECT_NE(id1, id2);
+}
+
+// Tests that thread-local buffers are allocated at the first buffer request.
+TEST(ThreadLocalBufferTest, InitialValue) {
+  struct TypeTagX;
+  using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>;
+
+  auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+  MessageBuffer<SendSlotX>::GetBuffer();
+  auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+
+  EXPECT_EQ(0U, value1);
+  EXPECT_NE(0U, value2);
+}
+
+// Tests that the underlying buffers are the same for a given index/type pair
+// and different across index/type combinations.
+TEST(ThreadLocalBufferTest, BackingBuffer) {
+  auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer();
+  auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer();
+  auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer();
+  auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer();
+
+  EXPECT_EQ(buffer1.data(), buffer2.data());
+  EXPECT_EQ(buffer3.data(), buffer4.data());
+  EXPECT_NE(buffer1.data(), buffer3.data());
+  EXPECT_NE(buffer2.data(), buffer4.data());
+}
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
new file mode 100644
index 0000000..325f33f
--- /dev/null
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -0,0 +1,1101 @@
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/variant.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct BaseType {
+  BaseType(int value) : value(value) {}
+  int value;
+};
+
+struct DerivedType : BaseType {
+  DerivedType(int value) : BaseType{value} {};
+};
+
+template <typename T>
+class TestType {
+ public:
+  TestType(const T& value) : value_(value) {}
+  TestType(T&& value) : value_(std::move(value)) {}
+  TestType(const TestType&) = default;
+  TestType(TestType&&) = default;
+
+  TestType& operator=(const TestType&) = default;
+  TestType& operator=(TestType&&) = default;
+
+  const T& get() const { return value_; }
+  T&& take() { return std::move(value_); }
+
+ private:
+  T value_;
+};
+
+template <typename T>
+class InstrumentType {
+ public:
+  InstrumentType(const T& value) : value_(value) { constructor_count_++; }
+  InstrumentType(T&& value) : value_(std::move(value)) { constructor_count_++; }
+  InstrumentType(const InstrumentType& other) : value_(other.value_) {
+    constructor_count_++;
+  }
+  InstrumentType(InstrumentType&& other) : value_(std::move(other.value_)) {
+    constructor_count_++;
+  }
+  InstrumentType(const TestType<T>& other) : value_(other.get()) {
+    constructor_count_++;
+  }
+  InstrumentType(TestType<T>&& other) : value_(other.take()) {
+    constructor_count_++;
+  }
+  ~InstrumentType() { destructor_count_++; }
+
+  InstrumentType& operator=(const InstrumentType& other) {
+    copy_assignment_count_++;
+    value_ = other.value_;
+    return *this;
+  }
+  InstrumentType& operator=(InstrumentType&& other) {
+    move_assignment_count_++;
+    value_ = std::move(other.value_);
+    return *this;
+  }
+
+  InstrumentType& operator=(const TestType<T>& other) {
+    copy_assignment_count_++;
+    value_ = other.get();
+    return *this;
+  }
+  InstrumentType& operator=(TestType<T>&& other) {
+    move_assignment_count_++;
+    value_ = other.take();
+    return *this;
+  }
+
+  static std::size_t constructor_count() { return constructor_count_; }
+  static std::size_t destructor_count() { return destructor_count_; }
+  static std::size_t move_assignment_count() { return move_assignment_count_; }
+  static std::size_t copy_assignment_count() { return copy_assignment_count_; }
+
+  const T& get() const { return value_; }
+  T&& take() { return std::move(value_); }
+
+  static void clear() {
+    constructor_count_ = 0;
+    destructor_count_ = 0;
+    move_assignment_count_ = 0;
+    copy_assignment_count_ = 0;
+  }
+
+ private:
+  T value_;
+
+  static std::size_t constructor_count_;
+  static std::size_t destructor_count_;
+  static std::size_t move_assignment_count_;
+  static std::size_t copy_assignment_count_;
+};
+
+template <typename T>
+std::size_t InstrumentType<T>::constructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::destructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::move_assignment_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::copy_assignment_count_ = 0;
+
+}  // anonymous namespace
+
+TEST(Variant, Assignment) {
+  // Assert basic type properties.
+  {
+    Variant<int, bool, float> v;
+    ASSERT_EQ(-1, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_FALSE(v.is<bool>());
+    ASSERT_FALSE(v.is<float>());
+  }
+
+  {
+    Variant<int, bool, float> v;
+    v = 10;
+    ASSERT_EQ(0, v.index());
+    ASSERT_TRUE(v.is<int>());
+    ASSERT_FALSE(v.is<bool>());
+    ASSERT_FALSE(v.is<float>());
+    EXPECT_EQ(10, std::get<int>(v));
+  }
+
+  {
+    Variant<int, bool, float> v;
+    v = false;
+    ASSERT_EQ(1, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_TRUE(v.is<bool>());
+    ASSERT_FALSE(v.is<float>());
+    EXPECT_EQ(false, std::get<bool>(v));
+  }
+
+  {
+    Variant<int, bool, float> v;
+    v = 1.0f;
+    ASSERT_EQ(2, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_FALSE(v.is<bool>());
+    ASSERT_TRUE(v.is<float>());
+    EXPECT_FLOAT_EQ(1.0f, std::get<float>(v));
+  }
+
+  {
+    Variant<int, bool, float> v;
+    // ERROR: More than one type is implicitly convertible from double.
+    // v = 1.0;
+    v = static_cast<float>(1.0);
+  }
+
+  {
+    Variant<int, bool, float> v;
+
+    double x = 1.1;
+    v = static_cast<float>(x);
+    ASSERT_EQ(2, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_FALSE(v.is<bool>());
+    ASSERT_TRUE(v.is<float>());
+    EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+  }
+
+  {
+    Variant<int, std::string> v;
+    ASSERT_EQ(-1, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_FALSE(v.is<std::string>());
+  }
+
+  {
+    Variant<int, std::string> v;
+    v = 20;
+    ASSERT_EQ(0, v.index());
+    ASSERT_TRUE(v.is<int>());
+    ASSERT_FALSE(v.is<std::string>());
+    EXPECT_EQ(20, std::get<int>(v));
+  }
+
+  {
+    Variant<int, std::string> v;
+    v = std::string("test");
+    ASSERT_EQ(1, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(v));
+  }
+
+  {
+    Variant<int, std::string> v;
+    v = "test";
+    ASSERT_EQ(1, v.index());
+    ASSERT_FALSE(v.is<int>());
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(v));
+  }
+
+  {
+    Variant<const char*> v1;
+    Variant<std::string> v2;
+
+    v1 = "test";
+    ASSERT_TRUE(v1.is<const char*>());
+    v2 = v1;
+    ASSERT_TRUE(v2.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(v2));
+  }
+
+  {
+    Variant<int> a(1);
+    Variant<int> b;
+    ASSERT_TRUE(!a.empty());
+    ASSERT_TRUE(b.empty());
+
+    a = b;
+    ASSERT_TRUE(a.empty());
+    ASSERT_TRUE(b.empty());
+  }
+
+  {
+    Variant<int*, char*> v;
+
+    // ERROR: More than one type is implicitly convertible from nullptr.
+    // v = nullptr;
+
+    v = static_cast<int*>(nullptr);
+    EXPECT_TRUE(v.is<int*>());
+
+    v = static_cast<char*>(nullptr);
+    EXPECT_TRUE(v.is<char*>());
+  }
+
+  {
+    Variant<int*, char*> v;
+    int a = 10;
+    char b = 20;
+
+    v = &b;
+    ASSERT_TRUE(v.is<char*>());
+    EXPECT_EQ(&b, std::get<char*>(v));
+    EXPECT_EQ(b, *std::get<char*>(v));
+
+    v = &a;
+    ASSERT_TRUE(v.is<int*>());
+    EXPECT_EQ(&a, std::get<int*>(v));
+    EXPECT_EQ(a, *std::get<int*>(v));
+  }
+
+  {
+    using IntRef = std::reference_wrapper<int>;
+    Variant<IntRef> v;
+    int a = 10;
+
+    v = a;
+    ASSERT_TRUE(v.is<IntRef>());
+    EXPECT_EQ(a, std::get<IntRef>(v));
+
+    a = 20;
+    EXPECT_EQ(a, std::get<IntRef>(v));
+  }
+}
+
+TEST(Variant, MoveAssignment) {
+  {
+    Variant<std::string> v;
+    std::string s = "test";
+    v = std::move(s);
+
+    EXPECT_TRUE(s.empty());
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(v));
+  }
+
+  {
+    Variant<std::string> v("test");
+    std::string s = "fizz";
+    s = std::move(std::get<std::string>(v));
+
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(v).empty());
+    EXPECT_EQ("test", s);
+  }
+
+  {
+    Variant<std::string> a("test");
+    Variant<std::string> b;
+
+    b = std::move(a);
+    ASSERT_TRUE(a.is<std::string>());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(a).empty());
+    EXPECT_EQ("test", std::get<std::string>(b));
+  }
+
+  {
+    Variant<std::string> a("test");
+    Variant<std::string> b("fizz");
+
+    b = std::move(a);
+    ASSERT_TRUE(a.is<std::string>());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(a).empty());
+    EXPECT_EQ("test", std::get<std::string>(b));
+  }
+
+  {
+    Variant<int, std::string> a("test");
+    Variant<int, std::string> b(10);
+
+    b = std::move(a);
+    ASSERT_TRUE(a.is<std::string>());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(a).empty());
+    EXPECT_EQ("test", std::get<std::string>(b));
+  }
+
+  {
+    Variant<int, std::string> a(10);
+    Variant<int, std::string> b("test");
+
+    b = std::move(a);
+    ASSERT_TRUE(a.is<int>());
+    ASSERT_TRUE(b.is<int>());
+    EXPECT_EQ(10, std::get<int>(a));
+    EXPECT_EQ(10, std::get<int>(b));
+  }
+}
+
+TEST(Variant, Constructor) {
+  {
+    Variant<int, bool, float> v(true);
+    EXPECT_TRUE(v.is<bool>());
+  }
+
+  {
+    Variant<int, bool, float> v(10);
+    EXPECT_TRUE(v.is<int>());
+  }
+
+  {
+    Variant<int, bool, float> v(10.1f);
+    EXPECT_TRUE(v.is<float>());
+  }
+
+  {
+    Variant<float, std::string> v(10.);
+    EXPECT_TRUE(v.is<float>());
+  }
+
+  {
+    TestType<int> i(1);
+    Variant<int, bool, float> v(i.take());
+    ASSERT_TRUE(v.is<int>());
+    EXPECT_EQ(1, std::get<int>(v));
+  }
+
+  {
+    TestType<int> i(1);
+    Variant<int, bool, float> v(i.get());
+    ASSERT_TRUE(v.is<int>());
+    EXPECT_EQ(1, std::get<int>(v));
+  }
+
+  {
+    TestType<bool> b(true);
+    Variant<int, bool, float> v(b.take());
+    ASSERT_TRUE(v.is<bool>());
+    EXPECT_EQ(true, std::get<bool>(v));
+  }
+
+  {
+    TestType<bool> b(true);
+    Variant<int, bool, float> v(b.get());
+    ASSERT_TRUE(v.is<bool>());
+    EXPECT_EQ(true, std::get<bool>(v));
+  }
+
+  {
+    Variant<const char*> c("test");
+    Variant<std::string> s(c);
+    ASSERT_TRUE(s.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(s));
+  }
+
+  {
+    Variant<int, bool, float> a(true);
+    Variant<int, bool, float> b(a);
+
+    ASSERT_TRUE(b.is<bool>());
+  }
+
+  {
+    using IntRef = std::reference_wrapper<int>;
+    int a = 10;
+    Variant<IntRef> v(a);
+    TestType<IntRef> t(a);
+
+    ASSERT_TRUE(v.is<IntRef>());
+    EXPECT_EQ(a, std::get<IntRef>(v));
+    EXPECT_EQ(a, t.get());
+
+    a = 20;
+    EXPECT_EQ(a, std::get<IntRef>(v));
+    EXPECT_EQ(a, t.get());
+  }
+}
+
+// Verify correct ctor/dtor and assignment behavior used an instrumented type.
+TEST(Variant, CopyMoveConstructAssign) {
+  {
+    InstrumentType<int>::clear();
+
+    // Default construct to empty, no InstrumentType activity.
+    Variant<int, InstrumentType<int>> v;
+    ASSERT_EQ(0u, InstrumentType<int>::constructor_count());
+    ASSERT_EQ(0u, InstrumentType<int>::destructor_count());
+    ASSERT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    ASSERT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from int type, no InstrumentType activity.
+    Variant<int, InstrumentType<int>> v;
+    v = 10;
+    EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from int type, no InstrumentType activity.
+    Variant<int, InstrumentType<int>> v(10);
+    EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v;
+    v = InstrumentType<int>(25);
+    EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+    EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+    // Assign from temporary, temporary ctor/dtor.
+    v = InstrumentType<int>(35);
+    EXPECT_EQ(3u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+    // dtor.
+    v = 10;
+    EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+    EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+  EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+  EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+  EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+  EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from other temporary.
+    Variant<int, InstrumentType<int>> v(TestType<int>(10));
+    EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from other temporary.
+    Variant<int, InstrumentType<int>> v(TestType<int>(10));
+    // Assign from other temporary.
+    v = TestType<int>(11);
+    EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from other temporary.
+    Variant<int, InstrumentType<int>> v(TestType<int>(10));
+    // Assign from empty Variant.
+    v = Variant<int, InstrumentType<int>>();
+    EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    TestType<int> other(10);
+    // Construct from other.
+    Variant<int, InstrumentType<int>> v(other);
+
+    EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from other temporary.
+    Variant<int, InstrumentType<int>> v(TestType<int>(0));
+    TestType<int> other(10);
+    // Assign from other.
+    v = other;
+    EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count());
+  }
+}
+
+TEST(Variant, MoveConstructor) {
+  {
+    std::unique_ptr<int> pointer = std::make_unique<int>(10);
+    Variant<std::unique_ptr<int>> v(std::move(pointer));
+    ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) != nullptr);
+    EXPECT_TRUE(pointer == nullptr);
+  }
+
+  {
+    Variant<std::unique_ptr<int>> a(std::make_unique<int>(10));
+    Variant<std::unique_ptr<int>> b(std::move(a));
+
+    ASSERT_TRUE(a.is<std::unique_ptr<int>>());
+    ASSERT_TRUE(b.is<std::unique_ptr<int>>());
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(a) == nullptr);
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(b) != nullptr);
+  }
+}
+
+TEST(Variant, IndexOf) {
+  Variant<int, bool, float> v1;
+
+  EXPECT_EQ(0, v1.index_of<int>());
+  EXPECT_EQ(1, v1.index_of<bool>());
+  EXPECT_EQ(2, v1.index_of<float>());
+
+  Variant<int, bool, float, int> v2;
+
+  EXPECT_EQ(0, v2.index_of<int>());
+  EXPECT_EQ(1, v2.index_of<bool>());
+  EXPECT_EQ(2, v2.index_of<float>());
+}
+
+struct Visitor {
+  int int_value = 0;
+  bool bool_value = false;
+  float float_value = 0.0;
+  bool empty_value = false;
+
+  void Visit(int value) { int_value = value; }
+  void Visit(bool value) { bool_value = value; }
+  void Visit(float value) { float_value = value; }
+  void Visit(EmptyVariant) { empty_value = true; }
+};
+
+TEST(Variant, Visit) {
+  {
+    Variant<int, bool, float> v(10);
+    EXPECT_TRUE(v.is<int>());
+
+    Visitor visitor;
+    v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+    EXPECT_EQ(10, visitor.int_value);
+
+    visitor = {};
+    v = true;
+    v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+    EXPECT_EQ(true, visitor.bool_value);
+  }
+
+  {
+    Variant<int, bool, float> v;
+    EXPECT_EQ(-1, v.index());
+
+    Visitor visitor;
+    v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+    EXPECT_TRUE(visitor.empty_value);
+  }
+
+  {
+    Variant<std::string> v("test");
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_FALSE(std::get<std::string>(v).empty());
+
+    v.Visit([](auto&& value) {
+      std::remove_reference_t<decltype(value)> empty;
+      std::swap(empty, value);
+    });
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(v).empty());
+  }
+}
+
+TEST(Variant, Become) {
+  {
+    Variant<int, bool, float> v;
+
+    v.Become(0);
+    EXPECT_TRUE(v.is<int>());
+
+    v.Become(1);
+    EXPECT_TRUE(v.is<bool>());
+
+    v.Become(2);
+    EXPECT_TRUE(v.is<float>());
+
+    v.Become(3);
+    EXPECT_TRUE(v.empty());
+
+    v.Become(-1);
+    EXPECT_TRUE(v.empty());
+
+    v.Become(-2);
+    EXPECT_TRUE(v.empty());
+  }
+
+  {
+    Variant<int, bool, float> v;
+
+    v.Become(0, 10);
+    ASSERT_TRUE(v.is<int>());
+    EXPECT_EQ(10, std::get<int>(v));
+
+    v.Become(1, true);
+    ASSERT_TRUE(v.is<bool>());
+    EXPECT_EQ(true, std::get<bool>(v));
+
+    v.Become(2, 2.0f);
+    ASSERT_TRUE(v.is<float>());
+    EXPECT_FLOAT_EQ(2.0f, std::get<float>(v));
+
+    v.Become(3, 10);
+    EXPECT_TRUE(v.empty());
+
+    v.Become(-1, 10);
+    EXPECT_TRUE(v.empty());
+
+    v.Become(-2, 20);
+    EXPECT_TRUE(v.empty());
+  }
+
+  {
+    Variant<std::string> v;
+
+    v.Become(0);
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_TRUE(std::get<std::string>(v).empty());
+  }
+
+  {
+    Variant<std::string> v;
+
+    v.Become(0, "test");
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("test", std::get<std::string>(v));
+  }
+
+  {
+    Variant<std::string> v("foo");
+
+    v.Become(0, "bar");
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("foo", std::get<std::string>(v));
+  }
+}
+
+TEST(Variant, Swap) {
+  {
+    Variant<std::string> a;
+    Variant<std::string> b;
+
+    std::swap(a, b);
+    EXPECT_TRUE(a.empty());
+    EXPECT_TRUE(b.empty());
+  }
+
+  {
+    Variant<std::string> a("1");
+    Variant<std::string> b;
+
+    std::swap(a, b);
+    EXPECT_TRUE(!a.empty());
+    EXPECT_TRUE(!b.empty());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_EQ("1", std::get<std::string>(b));
+  }
+
+  {
+    Variant<std::string> a;
+    Variant<std::string> b("1");
+
+    std::swap(a, b);
+    EXPECT_TRUE(!a.empty());
+    EXPECT_TRUE(!b.empty());
+    ASSERT_TRUE(a.is<std::string>());
+    EXPECT_EQ("1", std::get<std::string>(a));
+  }
+
+  {
+    Variant<std::string> a("1");
+    Variant<std::string> b("2");
+
+    std::swap(a, b);
+    ASSERT_TRUE(a.is<std::string>());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_EQ("2", std::get<std::string>(a));
+    EXPECT_EQ("1", std::get<std::string>(b));
+  }
+
+  {
+    Variant<int, std::string> a(10);
+    Variant<int, std::string> b("1");
+
+    std::swap(a, b);
+    ASSERT_TRUE(a.is<std::string>());
+    ASSERT_TRUE(b.is<int>());
+    EXPECT_EQ("1", std::get<std::string>(a));
+    EXPECT_EQ(10, std::get<int>(b));
+  }
+
+  {
+    Variant<int, std::string> a("1");
+    Variant<int, std::string> b(10);
+
+    std::swap(a, b);
+    ASSERT_TRUE(a.is<int>());
+    ASSERT_TRUE(b.is<std::string>());
+    EXPECT_EQ(10, std::get<int>(a));
+    EXPECT_EQ("1", std::get<std::string>(b));
+  }
+}
+
+TEST(Variant, Get) {
+  {
+    Variant<int, bool, float, int> v;
+
+    EXPECT_EQ(nullptr, &std::get<int>(v));
+    EXPECT_EQ(nullptr, &std::get<bool>(v));
+    EXPECT_EQ(nullptr, &std::get<float>(v));
+    EXPECT_EQ(nullptr, &std::get<0>(v));
+    EXPECT_EQ(nullptr, &std::get<1>(v));
+    EXPECT_EQ(nullptr, &std::get<2>(v));
+    EXPECT_EQ(nullptr, &std::get<3>(v));
+  }
+
+  {
+    Variant<int, bool, float, int> v;
+    v = 9;
+    ASSERT_TRUE(v.is<int>())
+        << "Expected type " << v.index_of<int>() << " got type " << v.index();
+    EXPECT_EQ(9, std::get<int>(v));
+    EXPECT_EQ(9, std::get<0>(v));
+
+    std::get<int>(v) = 10;
+    EXPECT_EQ(10, std::get<int>(v));
+    EXPECT_EQ(10, std::get<0>(v));
+
+    std::get<0>(v) = 11;
+    EXPECT_EQ(11, std::get<int>(v));
+    EXPECT_EQ(11, std::get<0>(v));
+
+    std::get<3>(v) = 12;
+    EXPECT_EQ(12, std::get<int>(v));
+    EXPECT_EQ(12, std::get<3>(v));
+  }
+
+  {
+    Variant<int, bool, float, int> v;
+    v = false;
+    ASSERT_TRUE(v.is<bool>())
+        << "Expected type " << v.index_of<bool>() << " got type " << v.index();
+    EXPECT_EQ(false, std::get<bool>(v));
+    EXPECT_EQ(false, std::get<1>(v));
+
+    std::get<bool>(v) = true;
+    EXPECT_EQ(true, std::get<bool>(v));
+    EXPECT_EQ(true, std::get<1>(v));
+
+    std::get<bool>(v) = false;
+    EXPECT_EQ(false, std::get<bool>(v));
+    EXPECT_EQ(false, std::get<1>(v));
+
+    std::get<1>(v) = true;
+    EXPECT_EQ(true, std::get<bool>(v));
+    EXPECT_EQ(true, std::get<1>(v));
+
+    std::get<1>(v) = false;
+    EXPECT_EQ(false, std::get<bool>(v));
+    EXPECT_EQ(false, std::get<1>(v));
+  }
+
+  {
+    Variant<int, bool, float, int> v;
+    v = 1.0f;
+    ASSERT_TRUE(v.is<float>())
+        << "Expected type " << v.index_of<float>() << " got type " << v.index();
+    EXPECT_EQ(2, v.index());
+    EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+    EXPECT_FLOAT_EQ(1.0, std::get<2>(v));
+
+    std::get<float>(v) = 1.1;
+    EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+    EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+    std::get<float>(v) = -3.0;
+    EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+    EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+
+    std::get<2>(v) = 1.1;
+    EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+    EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+    std::get<2>(v) = -3.0;
+    EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+    EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+  }
+
+  {
+    Variant<std::unique_ptr<int>> v(std::make_unique<int>(10));
+    std::unique_ptr<int> pointer = std::move(std::get<std::unique_ptr<int>>(v));
+    ASSERT_FALSE(v.empty());
+    EXPECT_TRUE(pointer != nullptr);
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+  }
+
+  {
+    Variant<std::string> v("test");
+    std::string s = std::get<std::string>(std::move(v));
+    EXPECT_EQ("test", s);
+  }
+}
+
+TEST(Variant, IfAnyOf) {
+  {
+    Variant<int, float> v(10);
+    ASSERT_TRUE(v.is<int>());
+
+    bool b = false;
+    EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+    EXPECT_TRUE(b);
+
+    float f = 0.0f;
+    EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+    EXPECT_FLOAT_EQ(10.f, f);
+  }
+
+  {
+    const Variant<int, float> v(10);
+    ASSERT_TRUE(v.is<int>());
+
+    bool b = false;
+    EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+    EXPECT_TRUE(b);
+
+    float f = 0.0f;
+    EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+    EXPECT_FLOAT_EQ(10.f, f);
+  }
+
+  {
+    Variant<int, float> v(10);
+    ASSERT_TRUE(v.is<int>());
+
+    bool b = false;
+    EXPECT_TRUE(IfAnyOf<int>::Call(&v, [&b](const auto& value) { b = value; }));
+    EXPECT_TRUE(b);
+
+    float f = 0.0f;
+    EXPECT_TRUE((
+        IfAnyOf<int, float>::Call(&v, [&f](const auto& value) { f = value; })));
+    EXPECT_FLOAT_EQ(10.f, f);
+  }
+
+  {
+    Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+    ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+    const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+    std::unique_ptr<int> u(std::make_unique<int>(20));
+
+    EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Take(&v, &u));
+    ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+    EXPECT_EQ(u.get(), original_v);
+  }
+
+  {
+    Variant<std::unique_ptr<DerivedType>, int> v(
+        std::make_unique<DerivedType>(10));
+    ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+    const DerivedType* original_v =
+        std::get<std::unique_ptr<DerivedType>>(v).get();
+
+    std::unique_ptr<BaseType> u(std::make_unique<BaseType>(20));
+
+    EXPECT_TRUE(IfAnyOf<std::unique_ptr<DerivedType>>::Take(&v, &u));
+    ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+    EXPECT_TRUE(std::get<std::unique_ptr<DerivedType>>(v) == nullptr);
+    EXPECT_EQ(u.get(), original_v);
+  }
+
+  {
+    Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+    ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+    const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+    std::unique_ptr<int> u(std::make_unique<int>(20));
+
+    EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Call(
+        &v, [&u](auto&& value) { u = std::move(value); }));
+    ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+    EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+    EXPECT_EQ(u.get(), original_v);
+  }
+
+  {
+    Variant<int, bool, float> v(true);
+    ASSERT_TRUE(v.is<bool>());
+
+    float f = 0.f;
+    EXPECT_FALSE((IfAnyOf<int, float>::Get(&v, &f)));
+    EXPECT_FLOAT_EQ(0.f, f);
+  }
+
+  {
+    Variant<std::string, int> v("foo");
+    ASSERT_TRUE(v.is<std::string>());
+
+    std::string s = "bar";
+    EXPECT_TRUE(IfAnyOf<std::string>::Swap(&v, &s));
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("bar", std::get<std::string>(v));
+    EXPECT_EQ("foo", s);
+  }
+
+  {
+    Variant<std::string, const char*> v(static_cast<const char*>("foo"));
+    ASSERT_TRUE(v.is<const char*>());
+
+    std::string s = "bar";
+    EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+    ASSERT_TRUE(v.is<const char*>());
+    EXPECT_EQ("foo", std::get<const char*>(v));
+    EXPECT_EQ("foo", s);
+
+    v = std::string("bar");
+    ASSERT_TRUE(v.is<std::string>());
+
+    EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+    ASSERT_TRUE(v.is<std::string>());
+    EXPECT_EQ("bar", s);
+  }
+
+  {
+    Variant<std::string, const char*> v;
+    ASSERT_TRUE(v.empty());
+
+    std::string s = "bar";
+    EXPECT_FALSE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+    EXPECT_EQ("bar", s);
+  }
+
+  {
+    Variant<std::string, const char*> v(static_cast<const char*>("test"));
+    ASSERT_TRUE(v.is<const char*>());
+
+    std::string s;
+    EXPECT_FALSE(IfAnyOf<>::Take(&v, &s));
+    EXPECT_TRUE(s.empty());
+  }
+}
+
+TEST(Variant, ConstVolatile) {
+  {
+    Variant<const int> v(10);
+    ASSERT_TRUE(v.is<const int>());
+    EXPECT_EQ(10, std::get<const int>(v));
+  }
+
+  {
+    Variant<const std::string> v("test");
+    ASSERT_TRUE(v.is<const std::string>());
+    EXPECT_EQ("test", std::get<const std::string>(v));
+  }
+
+  {
+    Variant<volatile int, std::string> v(10);
+    ASSERT_TRUE(v.is<volatile int>());
+    EXPECT_EQ(10, std::get<volatile int>(v));
+  }
+}
+
+TEST(Variant, HasType) {
+  EXPECT_TRUE((detail::HasType<int, int, float, bool>::value));
+  EXPECT_FALSE((detail::HasType<char, int, float, bool>::value));
+  EXPECT_FALSE(detail::HasType<>::value);
+
+  EXPECT_TRUE((detail::HasType<int&, int, float, bool>::value));
+  EXPECT_FALSE((detail::HasType<char&, int, float, bool>::value));
+}
+
+TEST(Variant, Set) {
+  EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool,
+                                                                float>::value));
+  EXPECT_TRUE(
+      (detail::Set<int, bool, float>::template IsSubset<bool, float>::value));
+  EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<float>::value));
+  EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<>::value));
+
+  EXPECT_FALSE(
+      (detail::Set<int, bool, float>::template IsSubset<int, bool, float,
+                                                        char>::value));
+  EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<bool, float,
+                                                                 char>::value));
+  EXPECT_FALSE(
+      (detail::Set<int, bool, float>::template IsSubset<float, char>::value));
+  EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<char>::value));
+
+  EXPECT_TRUE(detail::Set<>::template IsSubset<>::value);
+  EXPECT_FALSE(detail::Set<>::template IsSubset<int>::value);
+  EXPECT_FALSE((detail::Set<>::template IsSubset<int, float>::value));
+}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
new file mode 100644
index 0000000..8cfa86f
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -0,0 +1,70 @@
+cc_defaults {
+    name: "pdx_default_transport_compiler_defaults",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_defaults {
+    name: "pdx_default_transport_lib_defaults",
+    export_include_dirs: ["private"],
+    whole_static_libs: ["libpdx"],
+}
+
+cc_defaults {
+    name: "pdx_use_transport_servicefs",
+    export_include_dirs: ["private/servicefs"],
+    whole_static_libs: ["libpdx_servicefs", "libservicefs"],
+}
+
+cc_defaults {
+    name: "pdx_use_transport_uds",
+    export_include_dirs: ["private/uds"],
+    whole_static_libs: ["libpdx_uds"],
+}
+
+cc_library_static {
+    name: "libpdx_default_transport",
+    defaults: [
+        "pdx_default_transport_compiler_defaults",
+        "pdx_default_transport_lib_defaults",
+        "pdx_use_transport_uds",
+    ],
+}
+
+cc_binary {
+    name: "servicetool",
+    defaults: ["pdx_default_transport_compiler_defaults"],
+    srcs: [
+        "servicetool.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libpdx_default_transport",
+    ],
+}
+
+// Benchmarks.
+cc_binary {
+    name: "pdx_benchmarks",
+    defaults: ["pdx_default_transport_compiler_defaults"],
+    srcs: [
+        "pdx_benchmarks.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libchrome",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "libpdx_default_transport",
+    ],
+}
+
diff --git a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
new file mode 100644
index 0000000..fa0adf0
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
@@ -0,0 +1,1090 @@
+// Use ALWAYS at the tag level. Control is performed manually during command
+// line processing.
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <utils/Trace.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <cstdlib>
+#include <functional>
+#include <future>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::Endpoint;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Service;
+using android::pdx::ServiceBase;
+using android::pdx::default_transport::ClientChannelFactory;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DefaultInitializationAllocator;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodReturn;
+using android::pdx::rpc::ReplyBuffer;
+using android::pdx::rpc::Void;
+using android::pdx::rpc::WrapBuffer;
+
+namespace {
+
+constexpr size_t kMaxMessageSize = 4096 * 1024;
+
+std::string GetServicePath(const std::string& path, int instance_id) {
+  return path + std::to_string(instance_id);
+}
+
+void SetThreadName(const std::string& name) {
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0);
+}
+
+constexpr uint64_t kNanosPerSecond = 1000000000llu;
+
+uint64_t GetClockNs() {
+  timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  return kNanosPerSecond * t.tv_sec + t.tv_nsec;
+}
+
+template <typename T>
+ssize_t ssizeof(const T&) {
+  return static_cast<ssize_t>(sizeof(T));
+}
+
+class SchedStats {
+ public:
+  SchedStats() : SchedStats(gettid()) {}
+  SchedStats(pid_t task_id) : task_id_(task_id) {}
+  SchedStats(const SchedStats&) = default;
+  SchedStats& operator=(const SchedStats&) = default;
+
+  void Update() {
+    const std::string stats_path =
+        "/proc/" + std::to_string(task_id_) + "/schedstat";
+
+    std::string line;
+    base::ReadFileToString(base::FilePath{stats_path}, &line);
+    std::vector<std::string> stats = base::SplitString(
+        line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+    CHECK_EQ(3u, stats.size());
+
+    // Calculate the deltas since the last update. Each value is absolute since
+    // the task started.
+    uint64_t current_cpu_time_ns = std::stoull(stats[0]);
+    uint64_t current_wait_ns = std::stoull(stats[1]);
+    uint64_t current_timeslices = std::stoull(stats[2]);
+    cpu_time_ns_ = current_cpu_time_ns - last_cpu_time_ns_;
+    wait_ns_ = current_wait_ns - last_wait_ns_;
+    timeslices_ = current_timeslices - last_timeslices_;
+    last_cpu_time_ns_ = current_cpu_time_ns;
+    last_wait_ns_ = current_wait_ns;
+    last_timeslices_ = current_timeslices;
+  }
+
+  pid_t task_id() const { return task_id_; }
+  uint64_t cpu_time_ns() const { return cpu_time_ns_; }
+  uint64_t wait_ns() const { return wait_ns_; }
+  uint64_t timeslices() const { return timeslices_; }
+
+  double cpu_time_s() const {
+    return static_cast<double>(cpu_time_ns_) / kNanosPerSecond;
+  }
+  double wait_s() const {
+    return static_cast<double>(wait_ns_) / kNanosPerSecond;
+  }
+
+ private:
+  int32_t task_id_;
+  uint64_t cpu_time_ns_ = 0;
+  uint64_t last_cpu_time_ns_ = 0;
+  uint64_t wait_ns_ = 0;
+  uint64_t last_wait_ns_ = 0;
+  uint64_t timeslices_ = 0;
+  uint64_t last_timeslices_ = 0;
+
+  PDX_SERIALIZABLE_MEMBERS(SchedStats, task_id_, cpu_time_ns_, wait_ns_,
+                           timeslices_);
+};
+
+// Opcodes for client/service protocol.
+struct BenchmarkOps {
+  enum : int {
+    Nop,
+    Read,
+    Write,
+    Echo,
+    Stats,
+    WriteVector,
+    EchoVector,
+    Quit,
+  };
+};
+
+struct BenchmarkRPC {
+  PDX_REMOTE_METHOD(Stats, BenchmarkOps::Stats,
+                    std::tuple<uint64_t, uint64_t, SchedStats>(Void));
+  PDX_REMOTE_METHOD(WriteVector, BenchmarkOps::WriteVector,
+                    int(const BufferWrapper<std::vector<uint8_t>> data));
+  PDX_REMOTE_METHOD(EchoVector, BenchmarkOps::EchoVector,
+                    BufferWrapper<std::vector<uint8_t>>(
+                        const BufferWrapper<std::vector<uint8_t>> data));
+};
+
+struct BenchmarkResult {
+  int thread_id = 0;
+  int service_id = 0;
+  double time_delta_s = 0.0;
+  uint64_t bytes_sent = 0;
+  SchedStats sched_stats = {};
+};
+
+// Global command line option values.
+struct Options {
+  bool verbose = false;
+  int threads = 1;
+  int opcode = BenchmarkOps::Read;
+  int blocksize = 1;
+  int count = 1;
+  int instances = 1;
+  int timeout = 1;
+  int warmup = 0;
+} ProgramOptions;
+
+// Command line option names.
+const char kOptionService[] = "service";
+const char kOptionClient[] = "client";
+const char kOptionVerbose[] = "verbose";
+const char kOptionOpcode[] = "op";
+const char kOptionBlocksize[] = "bs";
+const char kOptionCount[] = "count";
+const char kOptionThreads[] = "threads";
+const char kOptionInstances[] = "instances";
+const char kOptionTimeout[] = "timeout";
+const char kOptionTrace[] = "trace";
+const char kOptionWarmup[] = "warmup";
+
+// getopt() long options.
+static option long_options[] = {
+    {kOptionService, required_argument, 0, 0},
+    {kOptionClient, required_argument, 0, 0},
+    {kOptionVerbose, no_argument, 0, 0},
+    {kOptionOpcode, required_argument, 0, 0},
+    {kOptionBlocksize, required_argument, 0, 0},
+    {kOptionCount, required_argument, 0, 0},
+    {kOptionThreads, required_argument, 0, 0},
+    {kOptionInstances, required_argument, 0, 0},
+    {kOptionTimeout, required_argument, 0, 0},
+    {kOptionTrace, no_argument, 0, 0},
+    {kOptionWarmup, required_argument, 0, 0},
+    {0, 0, 0, 0},
+};
+
+// Parses the argument for kOptionOpcode and sets the value of
+// ProgramOptions.opcode.
+void ParseOpcodeOption(const std::string& argument) {
+  if (argument == "read") {
+    ProgramOptions.opcode = BenchmarkOps::Read;
+  } else if (argument == "write") {
+    ProgramOptions.opcode = BenchmarkOps::Write;
+  } else if (argument == "echo") {
+    ProgramOptions.opcode = BenchmarkOps::Echo;
+  } else if (argument == "writevec") {
+    ProgramOptions.opcode = BenchmarkOps::WriteVector;
+  } else if (argument == "echovec") {
+    ProgramOptions.opcode = BenchmarkOps::EchoVector;
+  } else if (argument == "quit") {
+    ProgramOptions.opcode = BenchmarkOps::Quit;
+  } else if (argument == "nop") {
+    ProgramOptions.opcode = BenchmarkOps::Nop;
+  } else if (argument == "stats") {
+    ProgramOptions.opcode = BenchmarkOps::Stats;
+  } else {
+    ProgramOptions.opcode = std::stoi(argument);
+  }
+}
+
+// Implements the service side of the benchmark.
+class BenchmarkService : public ServiceBase<BenchmarkService> {
+ public:
+  std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+    VLOG(1) << "BenchmarkService::OnChannelCreate: cid="
+            << message.GetChannelId();
+    return nullptr;
+  }
+
+  void OnChannelClose(Message& message,
+                      const std::shared_ptr<Channel>& /*channel*/) override {
+    VLOG(1) << "BenchmarkService::OnChannelClose: cid="
+            << message.GetChannelId();
+  }
+
+  Status<void> HandleMessage(Message& message) override {
+    ATRACE_NAME("BenchmarkService::HandleMessage");
+
+    switch (message.GetOp()) {
+      case BenchmarkOps::Nop:
+        VLOG(1) << "BenchmarkService::HandleMessage: op=nop";
+        {
+          ATRACE_NAME("Reply");
+          CHECK(message.Reply(0));
+        }
+        return {};
+
+      case BenchmarkOps::Write: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=write send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        Status<void> status;
+        if (message.GetSendLength())
+          status = message.ReadAll(send_buffer.data(), message.GetSendLength());
+
+        {
+          ATRACE_NAME("Reply");
+          if (!status)
+            CHECK(message.ReplyError(status.error()));
+          else
+            CHECK(message.Reply(message.GetSendLength()));
+        }
+        return {};
+      }
+
+      case BenchmarkOps::Read: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=read send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        Status<void> status;
+        if (message.GetReceiveLength()) {
+          status = message.WriteAll(receive_buffer.data(),
+                                    message.GetReceiveLength());
+        }
+
+        {
+          ATRACE_NAME("Reply");
+          if (!status)
+            CHECK(message.ReplyError(status.error()));
+          else
+            CHECK(message.Reply(message.GetReceiveLength()));
+        }
+        return {};
+      }
+
+      case BenchmarkOps::Echo: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        Status<void> status;
+        if (message.GetSendLength())
+          status = message.ReadAll(send_buffer.data(), message.GetSendLength());
+
+        if (!status) {
+          CHECK(message.ReplyError(status.error()));
+          return {};
+        }
+
+        if (message.GetSendLength()) {
+          status =
+              message.WriteAll(send_buffer.data(), message.GetSendLength());
+        }
+
+        {
+          ATRACE_NAME("Reply");
+          if (!status)
+            CHECK(message.ReplyError(status.error()));
+          else
+            CHECK(message.Reply(message.GetSendLength()));
+        }
+        return {};
+      }
+
+      case BenchmarkOps::Stats: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        // Snapshot the stats when the message is received.
+        const uint64_t receive_time_ns = GetClockNs();
+        sched_stats_.Update();
+
+        // Use the RPC system to return the results.
+        RemoteMethodReturn<BenchmarkRPC::Stats>(
+            message, BenchmarkRPC::Stats::Return{receive_time_ns, GetClockNs(),
+                                                 sched_stats_});
+        return {};
+      }
+
+      case BenchmarkOps::WriteVector:
+        VLOG(1) << "BenchmarkService::HandleMessage: op=writevec send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        DispatchRemoteMethod<BenchmarkRPC::WriteVector>(
+            *this, &BenchmarkService::OnWriteVector, message, kMaxMessageSize);
+        return {};
+
+      case BenchmarkOps::EchoVector:
+        VLOG(1) << "BenchmarkService::HandleMessage: op=echovec send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        DispatchRemoteMethod<BenchmarkRPC::EchoVector>(
+            *this, &BenchmarkService::OnEchoVector, message, kMaxMessageSize);
+        return {};
+
+      case BenchmarkOps::Quit:
+        Cancel();
+        return ErrorStatus{ESHUTDOWN};
+
+      default:
+        VLOG(1) << "BenchmarkService::HandleMessage: default case; op="
+                << message.GetOp();
+        return Service::DefaultHandleMessage(message);
+    }
+  }
+
+  // Updates the scheduler stats from procfs for this thread.
+  void UpdateSchedStats() { sched_stats_.Update(); }
+
+ private:
+  friend BASE;
+
+  BenchmarkService(std::unique_ptr<Endpoint> endpoint)
+      : BASE("BenchmarkService", std::move(endpoint)),
+        send_buffer(kMaxMessageSize),
+        receive_buffer(kMaxMessageSize) {}
+
+  std::vector<uint8_t> send_buffer;
+  std::vector<uint8_t> receive_buffer;
+
+  // Each service thread has its own scheduler stats object.
+  static thread_local SchedStats sched_stats_;
+
+  using BufferType = BufferWrapper<
+      std::vector<uint8_t, DefaultInitializationAllocator<uint8_t>>>;
+
+  int OnWriteVector(Message&, const BufferType& data) { return data.size(); }
+  BufferType OnEchoVector(Message&, BufferType&& data) {
+    return std::move(data);
+  }
+
+  BenchmarkService(const BenchmarkService&) = delete;
+  void operator=(const BenchmarkService&) = delete;
+};
+
+thread_local SchedStats BenchmarkService::sched_stats_;
+
+// Implements the client side of the benchmark.
+class BenchmarkClient : public ClientBase<BenchmarkClient> {
+ public:
+  int Nop() {
+    ATRACE_NAME("BenchmarkClient::Nop");
+    VLOG(1) << "BenchmarkClient::Nop";
+    Transaction transaction{*this};
+    return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Nop));
+  }
+
+  int Write(const void* buffer, size_t length) {
+    ATRACE_NAME("BenchmarkClient::Write");
+    VLOG(1) << "BenchmarkClient::Write: buffer=" << buffer
+            << " length=" << length;
+    Transaction transaction{*this};
+    return ReturnStatusOrError(
+        transaction.Send<int>(BenchmarkOps::Write, buffer, length, nullptr, 0));
+    // return write(endpoint_fd(), buffer, length);
+  }
+
+  int Read(void* buffer, size_t length) {
+    ATRACE_NAME("BenchmarkClient::Read");
+    VLOG(1) << "BenchmarkClient::Read: buffer=" << buffer
+            << " length=" << length;
+    Transaction transaction{*this};
+    return ReturnStatusOrError(
+        transaction.Send<int>(BenchmarkOps::Read, nullptr, 0, buffer, length));
+    // return read(endpoint_fd(), buffer, length);
+  }
+
+  int Echo(const void* send_buffer, size_t send_length, void* receive_buffer,
+           size_t receive_length) {
+    ATRACE_NAME("BenchmarkClient::Echo");
+    VLOG(1) << "BenchmarkClient::Echo: send_buffer=" << send_buffer
+            << " send_length=" << send_length
+            << " receive_buffer=" << receive_buffer
+            << " receive_length=" << receive_length;
+    Transaction transaction{*this};
+    return ReturnStatusOrError(
+        transaction.Send<int>(BenchmarkOps::Echo, send_buffer, send_length,
+                              receive_buffer, receive_length));
+  }
+
+  int Stats(std::tuple<uint64_t, uint64_t, SchedStats>* stats_out) {
+    ATRACE_NAME("BenchmarkClient::Stats");
+    VLOG(1) << "BenchmarkClient::Stats";
+
+    auto status = InvokeRemoteMethodInPlace<BenchmarkRPC::Stats>(stats_out);
+    return status ? 0 : -status.error();
+  }
+
+  int WriteVector(const BufferWrapper<std::vector<uint8_t>>& data) {
+    ATRACE_NAME("BenchmarkClient::Stats");
+    VLOG(1) << "BenchmarkClient::Stats";
+
+    auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+    return ReturnStatusOrError(status);
+  }
+
+  template <typename T>
+  int WriteVector(const BufferWrapper<T>& data) {
+    ATRACE_NAME("BenchmarkClient::WriteVector");
+    VLOG(1) << "BenchmarkClient::WriteVector";
+
+    auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+    return ReturnStatusOrError(status);
+  }
+
+  template <typename T, typename U>
+  int EchoVector(const BufferWrapper<T>& data, BufferWrapper<U>* data_out) {
+    ATRACE_NAME("BenchmarkClient::EchoVector");
+    VLOG(1) << "BenchmarkClient::EchoVector";
+
+    MessageBuffer<ReplyBuffer>::Reserve(kMaxMessageSize - 1);
+    auto status =
+        InvokeRemoteMethodInPlace<BenchmarkRPC::EchoVector>(data_out, data);
+    return status ? 0 : -status.error();
+  }
+
+  int Quit() {
+    VLOG(1) << "BenchmarkClient::Quit";
+    Transaction transaction{*this};
+    return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Echo));
+  }
+
+ private:
+  friend BASE;
+
+  BenchmarkClient(const std::string& service_path)
+      : BASE(ClientChannelFactory::Create(service_path),
+             ProgramOptions.timeout) {}
+
+  BenchmarkClient(const BenchmarkClient&) = delete;
+  void operator=(const BenchmarkClient&) = delete;
+};
+
+// Creates a benchmark service at |path| and dispatches messages.
+int ServiceCommand(const std::string& path) {
+  if (path.empty())
+    return -EINVAL;
+
+  // Start the requested number of dispatch threads.
+  std::vector<std::thread> dispatch_threads;
+  int service_count = ProgramOptions.instances;
+  int service_id_counter = 0;
+  int thread_id_counter = 0;
+  std::atomic<bool> done(false);
+
+  while (service_count--) {
+    std::cerr << "Starting service instance " << service_id_counter
+              << std::endl;
+    auto service = BenchmarkService::Create(
+        android::pdx::default_transport::Endpoint::CreateAndBindSocket(
+            GetServicePath(path, service_id_counter),
+            android::pdx::default_transport::Endpoint::kBlocking));
+    if (!service) {
+      std::cerr << "Failed to create service instance!!" << std::endl;
+      done = true;
+      break;
+    }
+
+    int thread_count = ProgramOptions.threads;
+    while (thread_count--) {
+      std::cerr << "Starting dispatch thread " << thread_id_counter
+                << " service " << service_id_counter << std::endl;
+
+      dispatch_threads.emplace_back(
+          [&](const int thread_id, const int service_id,
+              const std::shared_ptr<BenchmarkService>& local_service) {
+            SetThreadName("service" + std::to_string(service_id));
+
+            // Read the initial schedstats for this thread from procfs.
+            local_service->UpdateSchedStats();
+
+            ATRACE_NAME("BenchmarkService::Dispatch");
+            while (!done) {
+              auto ret = local_service->ReceiveAndDispatch();
+              if (!ret) {
+                if (ret.error() != ESHUTDOWN) {
+                  std::cerr << "Error while dispatching message on thread "
+                            << thread_id << " service " << service_id << ": "
+                            << ret.GetErrorMessage() << std::endl;
+                } else {
+                  std::cerr << "Quitting thread " << thread_id << " service "
+                            << service_id << std::endl;
+                }
+                done = true;
+                return;
+              }
+            }
+          },
+          thread_id_counter++, service_id_counter, service);
+    }
+
+    service_id_counter++;
+  }
+
+  // Wait for the dispatch threads to exit.
+  for (auto& thread : dispatch_threads) {
+    thread.join();
+  }
+
+  return 0;
+}
+
+int ClientCommand(const std::string& path) {
+  // Start the requested number of client threads.
+  std::vector<std::thread> client_threads;
+  std::vector<std::future<BenchmarkResult>> client_results;
+  int service_count = ProgramOptions.instances;
+  int thread_id_counter = 0;
+  int service_id_counter = 0;
+
+  // Aggregate statistics, updated when worker threads exit.
+  std::atomic<uint64_t> total_bytes(0);
+  std::atomic<uint64_t> total_time_ns(0);
+
+  // Samples for variance calculation.
+  std::vector<uint64_t> latency_samples_ns(
+      ProgramOptions.instances * ProgramOptions.threads * ProgramOptions.count);
+  const size_t samples_per_thread = ProgramOptions.count;
+
+  std::vector<uint8_t> send_buffer(ProgramOptions.blocksize);
+  std::vector<uint8_t> receive_buffer(kMaxMessageSize);
+
+  // Barriers for synchronizing thread start.
+  std::vector<std::future<void>> ready_barrier_futures;
+  std::promise<void> go_barrier_promise;
+  std::future<void> go_barrier_future = go_barrier_promise.get_future();
+
+  // Barrier for synchronizing thread tear down.
+  std::promise<void> done_barrier_promise;
+  std::future<void> done_barrier_future = done_barrier_promise.get_future();
+
+  while (service_count--) {
+    int thread_count = ProgramOptions.threads;
+    while (thread_count--) {
+      std::cerr << "Starting client thread " << thread_id_counter << " service "
+                << service_id_counter << std::endl;
+
+      std::promise<BenchmarkResult> result_promise;
+      client_results.push_back(result_promise.get_future());
+
+      std::promise<void> ready_barrier_promise;
+      ready_barrier_futures.push_back(ready_barrier_promise.get_future());
+
+      client_threads.emplace_back(
+          [&](const int thread_id, const int service_id,
+              std::promise<BenchmarkResult> result, std::promise<void> ready) {
+            SetThreadName("client" + std::to_string(thread_id) + "/" +
+                          std::to_string(service_id));
+
+            ATRACE_NAME("BenchmarkClient::Dispatch");
+
+            auto client =
+                BenchmarkClient::Create(GetServicePath(path, service_id));
+            if (!client) {
+              std::cerr << "Failed to create client for service " << service_id
+                        << std::endl;
+              return -ENOMEM;
+            }
+
+            uint64_t* thread_samples =
+                &latency_samples_ns[samples_per_thread * thread_id];
+
+            // Per-thread statistics.
+            uint64_t bytes_sent = 0;
+            uint64_t time_start_ns;
+            uint64_t time_end_ns;
+            SchedStats sched_stats;
+
+            // Signal ready and wait for go.
+            ready.set_value();
+            go_barrier_future.wait();
+
+            // Warmup the scheduler.
+            int warmup = ProgramOptions.warmup;
+            while (warmup--) {
+              for (int i = 0; i < 1000000; i++)
+                ;
+            }
+
+            sched_stats.Update();
+            time_start_ns = GetClockNs();
+
+            int count = ProgramOptions.count;
+            while (count--) {
+              uint64_t iteration_start_ns = GetClockNs();
+
+              switch (ProgramOptions.opcode) {
+                case BenchmarkOps::Nop: {
+                  const int ret = client->Nop();
+                  if (ret < 0) {
+                    std::cerr << "Failed to send nop: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else {
+                    VLOG(1) << "Success";
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::Read: {
+                  const int ret = client->Read(receive_buffer.data(),
+                                               ProgramOptions.blocksize);
+                  if (ret < 0) {
+                    std::cerr << "Failed to read: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else if (ret != ProgramOptions.blocksize) {
+                    std::cerr << "Expected ret=" << ProgramOptions.blocksize
+                              << "; actual ret=" << ret << std::endl;
+                    return -EINVAL;
+                  } else {
+                    VLOG(1) << "Success";
+                    bytes_sent += ret;
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::Write: {
+                  const int ret =
+                      client->Write(send_buffer.data(), send_buffer.size());
+                  if (ret < 0) {
+                    std::cerr << "Failed to write: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else if (ret != ProgramOptions.blocksize) {
+                    std::cerr << "Expected ret=" << ProgramOptions.blocksize
+                              << "; actual ret=" << ret << std::endl;
+                    return -EINVAL;
+                  } else {
+                    VLOG(1) << "Success";
+                    bytes_sent += ret;
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::Echo: {
+                  const int ret = client->Echo(
+                      send_buffer.data(), send_buffer.size(),
+                      receive_buffer.data(), receive_buffer.size());
+                  if (ret < 0) {
+                    std::cerr << "Failed to echo: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else if (ret != ProgramOptions.blocksize) {
+                    std::cerr << "Expected ret=" << ProgramOptions.blocksize
+                              << "; actual ret=" << ret << std::endl;
+                    return -EINVAL;
+                  } else {
+                    VLOG(1) << "Success";
+                    bytes_sent += ret * 2;
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::Stats: {
+                  std::tuple<uint64_t, uint64_t, SchedStats> stats;
+                  const int ret = client->Stats(&stats);
+                  if (ret < 0) {
+                    std::cerr << "Failed to get stats: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else {
+                    VLOG(1) << "Success";
+                    std::cerr
+                        << "Round trip: receive_time_ns=" << std::get<0>(stats)
+                        << " reply_time_ns=" << std::get<1>(stats)
+                        << " cpu_time_s=" << std::get<2>(stats).cpu_time_s()
+                        << " wait_s=" << std::get<2>(stats).wait_s()
+                        << std::endl;
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::WriteVector: {
+                  const int ret = client->WriteVector(
+                      WrapBuffer(send_buffer.data(), ProgramOptions.blocksize));
+                  if (ret < 0) {
+                    std::cerr << "Failed to write vector: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else {
+                    VLOG(1) << "Success";
+                    bytes_sent += ret;
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::EchoVector: {
+                  thread_local BufferWrapper<std::vector<
+                      uint8_t, DefaultInitializationAllocator<uint8_t>>>
+                      response_buffer;
+                  const int ret = client->EchoVector(
+                      WrapBuffer(send_buffer.data(), ProgramOptions.blocksize),
+                      &response_buffer);
+                  if (ret < 0) {
+                    std::cerr << "Failed to echo vector: " << strerror(-ret)
+                              << std::endl;
+                    return ret;
+                  } else {
+                    VLOG(1) << "Success";
+                    bytes_sent += send_buffer.size() + response_buffer.size();
+                  }
+                  break;
+                }
+
+                case BenchmarkOps::Quit: {
+                  const int ret = client->Quit();
+                  if (ret < 0 && ret != -ESHUTDOWN) {
+                    std::cerr << "Failed to send quit: " << strerror(-ret);
+                    return ret;
+                  } else {
+                    VLOG(1) << "Success";
+                  }
+                  break;
+                }
+
+                default:
+                  std::cerr
+                      << "Invalid client operation: " << ProgramOptions.opcode
+                      << std::endl;
+                  return -EINVAL;
+              }
+
+              uint64_t iteration_end_ns = GetClockNs();
+              uint64_t iteration_delta_ns =
+                  iteration_end_ns - iteration_start_ns;
+              thread_samples[count] = iteration_delta_ns;
+
+              if (iteration_delta_ns > (kNanosPerSecond / 100)) {
+                SchedStats stats = sched_stats;
+                stats.Update();
+                std::cerr << "Thread " << thread_id << " iteration_delta_s="
+                          << (static_cast<double>(iteration_delta_ns) /
+                              kNanosPerSecond)
+                          << " " << stats.cpu_time_s() << " " << stats.wait_s()
+                          << std::endl;
+              }
+            }
+
+            time_end_ns = GetClockNs();
+            sched_stats.Update();
+
+            const double time_delta_s =
+                static_cast<double>(time_end_ns - time_start_ns) /
+                kNanosPerSecond;
+
+            total_bytes += bytes_sent;
+            total_time_ns += time_end_ns - time_start_ns;
+
+            result.set_value(
+                {thread_id, service_id, time_delta_s, bytes_sent, sched_stats});
+            done_barrier_future.wait();
+
+            return 0;
+          },
+          thread_id_counter++, service_id_counter, std::move(result_promise),
+          std::move(ready_barrier_promise));
+    }
+
+    service_id_counter++;
+  }
+
+  // Wait for workers to be ready.
+  std::cerr << "Waiting for workers to be ready..." << std::endl;
+  for (auto& ready : ready_barrier_futures)
+    ready.wait();
+
+  // Signal workers to go.
+  std::cerr << "Kicking off benchmark." << std::endl;
+  go_barrier_promise.set_value();
+
+  // Wait for all the worker threas to finish.
+  for (auto& result : client_results)
+    result.wait();
+
+  // Report worker thread results.
+  for (auto& result : client_results) {
+    BenchmarkResult benchmark_result = result.get();
+    std::cerr << std::fixed << "Thread " << benchmark_result.thread_id
+              << " service " << benchmark_result.service_id << ":" << std::endl;
+    std::cerr << "\t " << benchmark_result.bytes_sent << " bytes in "
+              << benchmark_result.time_delta_s << " seconds ("
+              << std::setprecision(0) << (benchmark_result.bytes_sent / 1024.0 /
+                                          benchmark_result.time_delta_s)
+              << " K/s; " << std::setprecision(3)
+              << (ProgramOptions.count / benchmark_result.time_delta_s)
+              << " txn/s; " << std::setprecision(9)
+              << (benchmark_result.time_delta_s / ProgramOptions.count)
+              << " s/txn)" << std::endl;
+    std::cerr << "\tStats: " << benchmark_result.sched_stats.cpu_time_s() << " "
+              << (benchmark_result.sched_stats.cpu_time_s() /
+                  ProgramOptions.count)
+              << " " << benchmark_result.sched_stats.wait_s() << " "
+              << (benchmark_result.sched_stats.wait_s() / ProgramOptions.count)
+              << " " << benchmark_result.sched_stats.timeslices() << std::endl;
+  }
+
+  // Signal worker threads to exit.
+  done_barrier_promise.set_value();
+
+  // Wait for the worker threads to exit.
+  for (auto& thread : client_threads) {
+    thread.join();
+  }
+
+  // Report aggregate results.
+  const int total_threads = ProgramOptions.threads * ProgramOptions.instances;
+  const int iterations = ProgramOptions.count;
+  const double total_time_s =
+      static_cast<double>(total_time_ns) / kNanosPerSecond;
+  // This is about how much wall time it took to completely transfer all the
+  // paylaods.
+  const double average_time_s = total_time_s / total_threads;
+
+  const uint64_t min_sample_time_ns =
+      *std::min_element(latency_samples_ns.begin(), latency_samples_ns.end());
+  const double min_sample_time_s =
+      static_cast<double>(min_sample_time_ns) / kNanosPerSecond;
+
+  const uint64_t max_sample_time_ns =
+      *std::max_element(latency_samples_ns.begin(), latency_samples_ns.end());
+  const double max_sample_time_s =
+      static_cast<double>(max_sample_time_ns) / kNanosPerSecond;
+
+  const double total_sample_time_s =
+      std::accumulate(latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+                      [](double s, uint64_t ns) {
+                        return s + static_cast<double>(ns) / kNanosPerSecond;
+                      });
+  const double average_sample_time_s =
+      total_sample_time_s / latency_samples_ns.size();
+
+  const double sum_of_squared_deviations = std::accumulate(
+      latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+      [&](double s, uint64_t ns) {
+        const double delta =
+            static_cast<double>(ns) / kNanosPerSecond - average_sample_time_s;
+        return s + delta * delta;
+      });
+  const double variance = sum_of_squared_deviations / latency_samples_ns.size();
+  const double standard_deviation = std::sqrt(variance);
+
+  const int num_buckets = 200;
+  const uint64_t sample_range_ns = max_sample_time_ns - min_sample_time_ns;
+  const uint64_t ns_per_bucket = sample_range_ns / num_buckets;
+  std::array<uint64_t, num_buckets> sample_buckets = {{0}};
+
+  // Count samples in each bucket range.
+  for (uint64_t sample_ns : latency_samples_ns) {
+    sample_buckets[(sample_ns - min_sample_time_ns) / (ns_per_bucket + 1)] += 1;
+  }
+
+  // Calculate population percentiles.
+  const uint64_t percent_50 =
+      static_cast<uint64_t>(latency_samples_ns.size() * 0.5);
+  const uint64_t percent_90 =
+      static_cast<uint64_t>(latency_samples_ns.size() * 0.9);
+  const uint64_t percent_95 =
+      static_cast<uint64_t>(latency_samples_ns.size() * 0.95);
+  const uint64_t percent_99 =
+      static_cast<uint64_t>(latency_samples_ns.size() * 0.99);
+
+  uint64_t sample_count = 0;
+  double latency_50th_percentile_s, latency_90th_percentile_s,
+      latency_95th_percentile_s, latency_99th_percentile_s;
+  for (int i = 0; i < num_buckets; i++) {
+    // Report the midpoint of the bucket range as the value of the
+    // corresponding
+    // percentile.
+    const double bucket_midpoint_time_s =
+        (ns_per_bucket * i + 0.5 * ns_per_bucket + min_sample_time_ns) /
+        kNanosPerSecond;
+    if (sample_count < percent_50 &&
+        (sample_count + sample_buckets[i]) >= percent_50) {
+      latency_50th_percentile_s = bucket_midpoint_time_s;
+    }
+    if (sample_count < percent_90 &&
+        (sample_count + sample_buckets[i]) >= percent_90) {
+      latency_90th_percentile_s = bucket_midpoint_time_s;
+    }
+    if (sample_count < percent_95 &&
+        (sample_count + sample_buckets[i]) >= percent_95) {
+      latency_95th_percentile_s = bucket_midpoint_time_s;
+    }
+    if (sample_count < percent_99 &&
+        (sample_count + sample_buckets[i]) >= percent_99) {
+      latency_99th_percentile_s = bucket_midpoint_time_s;
+    }
+    sample_count += sample_buckets[i];
+  }
+
+  std::cerr << std::fixed << "Total throughput over " << total_threads
+            << " threads:\n\t " << total_bytes << " bytes in " << average_time_s
+            << " seconds (" << std::setprecision(0)
+            << (total_bytes / 1024.0 / average_time_s) << " K/s; "
+            << std::setprecision(3)
+            << (iterations * total_threads / average_time_s)
+            << std::setprecision(9) << " txn/s; "
+            << (average_time_s / (iterations * total_threads)) << " s/txn)"
+            << std::endl;
+  std::cerr << "Sample statistics: " << std::endl;
+  std::cerr << total_sample_time_s << " s total sample time" << std::endl;
+  std::cerr << average_sample_time_s << " s avg" << std::endl;
+  std::cerr << standard_deviation << " s std dev" << std::endl;
+  std::cerr << min_sample_time_s << " s min" << std::endl;
+  std::cerr << max_sample_time_s << " s max" << std::endl;
+  std::cerr << "Latency percentiles:" << std::endl;
+  std::cerr << "50th: " << latency_50th_percentile_s << " s" << std::endl;
+  std::cerr << "90th: " << latency_90th_percentile_s << " s" << std::endl;
+  std::cerr << "95th: " << latency_95th_percentile_s << " s" << std::endl;
+  std::cerr << "99th: " << latency_99th_percentile_s << " s" << std::endl;
+
+  std::cout << total_time_ns << " " << std::fixed << std::setprecision(9)
+            << average_sample_time_s << " " << std::fixed
+            << std::setprecision(9) << standard_deviation << std::endl;
+  return 0;
+}
+
+int Usage(const std::string& command_name) {
+  // clang-format off
+  std::cout << "Usage: " << command_name << " [options]" << std::endl;
+  std::cout << "\t--verbose                   : Use verbose messages." << std::endl;
+  std::cout << "\t--service <endpoint path>   : Start service at the given path." << std::endl;
+  std::cout << "\t--client <endpoint path>    : Start client to the given path." << std::endl;
+  std::cout << "\t--op <read | write | echo>  : Sepcify client operation mode." << std::endl;
+  std::cout << "\t--bs <block size bytes>     : Sepcify block size to use." << std::endl;
+  std::cout << "\t--count <count>             : Sepcify number of transactions to make." << std::endl;
+  std::cout << "\t--instances <count>         : Specify number of service instances." << std::endl;
+  std::cout << "\t--threads <count>           : Sepcify number of threads per instance." << std::endl;
+  std::cout << "\t--timeout <timeout ms | -1> : Timeout to wait for services." << std::endl;
+  std::cout << "\t--trace                     : Enable systrace logging." << std::endl;
+  std::cout << "\t--warmup <iterations>       : Busy loops before running benchmarks." << std::endl;
+  // clang-format on
+  return -1;
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  logging::LoggingSettings logging_settings;
+  logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(logging_settings);
+
+  int getopt_code;
+  int option_index;
+  std::string option = "";
+  std::string command = "";
+  std::string command_argument = "";
+  bool tracing_enabled = false;
+
+  // Process command line options.
+  while ((getopt_code =
+              getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+    option = long_options[option_index].name;
+    VLOG(1) << "option=" << option;
+    switch (getopt_code) {
+      case 0:
+        if (option == kOptionVerbose) {
+          ProgramOptions.verbose = true;
+          logging::SetMinLogLevel(-1);
+        } else if (option == kOptionOpcode) {
+          ParseOpcodeOption(optarg);
+        } else if (option == kOptionBlocksize) {
+          ProgramOptions.blocksize = std::stoi(optarg);
+          if (ProgramOptions.blocksize < 0) {
+            std::cerr << "Invalid blocksize argument: "
+                      << ProgramOptions.blocksize << std::endl;
+            return -EINVAL;
+          }
+        } else if (option == kOptionCount) {
+          ProgramOptions.count = std::stoi(optarg);
+          if (ProgramOptions.count < 1) {
+            std::cerr << "Invalid count argument: " << ProgramOptions.count
+                      << std::endl;
+            return -EINVAL;
+          }
+        } else if (option == kOptionThreads) {
+          ProgramOptions.threads = std::stoi(optarg);
+          if (ProgramOptions.threads < 1) {
+            std::cerr << "Invalid threads argument: " << ProgramOptions.threads
+                      << std::endl;
+            return -EINVAL;
+          }
+        } else if (option == kOptionInstances) {
+          ProgramOptions.instances = std::stoi(optarg);
+          if (ProgramOptions.instances < 1) {
+            std::cerr << "Invalid instances argument: "
+                      << ProgramOptions.instances << std::endl;
+            return -EINVAL;
+          }
+        } else if (option == kOptionTimeout) {
+          ProgramOptions.timeout = std::stoi(optarg);
+        } else if (option == kOptionTrace) {
+          tracing_enabled = true;
+        } else if (option == kOptionWarmup) {
+          ProgramOptions.warmup = std::stoi(optarg);
+        } else {
+          command = option;
+          if (optarg)
+            command_argument = optarg;
+        }
+        break;
+    }
+  }
+
+  // Setup ATRACE/systrace based on command line.
+  atrace_setup();
+  atrace_set_tracing_enabled(tracing_enabled);
+
+  VLOG(1) << "command=" << command << " command_argument=" << command_argument;
+
+  if (command == "") {
+    return Usage(argv[0]);
+  } else if (command == kOptionService) {
+    return ServiceCommand(command_argument);
+  } else if (command == kOptionClient) {
+    return ClientCommand(command_argument);
+  } else {
+    return Usage(argv[0]);
+  }
+}
diff --git a/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
new file mode 100644
index 0000000..81bb17b
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+
+#include <ftw.h>
+
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/service.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+class ServiceUtility : public ClientBase<ServiceUtility> {
+ public:
+  Status<int> ReloadSystemProperties() {
+    Transaction transaction{*this};
+    return ReturnStatusOrError(
+        transaction.Send<int>(opcodes::REPORT_SYSPROP_CHANGE));
+  }
+
+  static std::string GetRootEndpointPath() {
+    return ClientChannelFactory::GetRootEndpointPath();
+  }
+  static std::string GetEndpointPath(const std::string& endpoint_path) {
+    return ClientChannelFactory::GetEndpointPath(endpoint_path);
+  }
+
+  // Traverses the PDX service path space and sends a message to reload system
+  // properties to each service endpoint it finds along the way.
+  // NOTE: This method is used by atrace to poke PDX services. Please avoid
+  // unnecessary changes to this mechanism to minimize impact on atrace.
+  static bool PokeServices() {
+    const int kMaxDepth = 16;
+    const int result =
+        nftw(GetRootEndpointPath().c_str(), PokeService, kMaxDepth, FTW_PHYS);
+    return result == 0 ? true : false;
+  }
+
+ private:
+  friend BASE;
+
+  ServiceUtility(const std::string& endpoint_path, int* error = nullptr)
+      : BASE(ClientChannelFactory::Create(endpoint_path), 0) {
+    if (error)
+      *error = Client::error();
+  }
+
+  // Sends the sysprop_change message to the service at fpath, so it re-reads
+  // its system properties. Returns 0 on success or a negated errno code on
+  // failure.
+  // NOTE: This method is used by atrace to poke PDX services. Please avoid
+  // unnecessary changes to this mechanism to minimize impact on atrace.
+  static int PokeService(const char* fpath, const struct stat* /*sb*/,
+                         int typeflag, struct FTW* /*ftwbuf*/) {
+    const bool kIgnoreErrors = true;
+
+    if (typeflag == FTW_F) {
+      int error;
+      auto utility = ServiceUtility::Create(fpath, &error);
+      if (!utility) {
+        if (error != -ECONNREFUSED) {
+          ALOGE("ServiceUtility::PokeService: Failed to open %s: %s.", fpath,
+                strerror(-error));
+        }
+        return kIgnoreErrors ? 0 : error;
+      }
+
+      auto status = utility->ReloadSystemProperties();
+      if (!status) {
+        ALOGE(
+            "ServiceUtility::PokeService: Failed to send sysprop change to %s: "
+            "%s",
+            fpath, status.GetErrorMessage().c_str());
+        return kIgnoreErrors ? 0 : -status.error();
+      }
+    }
+
+    return 0;
+  }
+
+  ServiceUtility(const ServiceUtility&) = delete;
+  void operator=(const ServiceUtility&) = delete;
+};
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000..11163b3
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+
+#include <servicefs/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::servicefs::ChannelManager;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000..d171780
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+
+#include <servicefs/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::servicefs::ClientChannel;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000..77b5cac
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <servicefs/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::servicefs::ClientChannelFactory;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
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
new file mode 100644
index 0000000..158871c
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#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/servicefs/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000..8f413c1
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+
+#include <servicefs/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::servicefs::Endpoint;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000..f34636f
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+
+#include <uds/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::uds::ChannelManager;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000..bf632d7
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+
+#include <uds/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::uds::ClientChannel;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000..e5c4e30
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <uds/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::uds::ClientChannelFactory;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_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
new file mode 100644
index 0000000..7cb7a80
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#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_default_transport/private/uds/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000..1fd6103
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::uds::Endpoint;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/servicetool.cpp
new file mode 100644
index 0000000..60eedb3
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/servicetool.cpp
@@ -0,0 +1,244 @@
+#include <errno.h>
+#include <ftw.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <pdx/default_transport/client_channel_factory.h>
+
+using android::pdx::default_transport::ClientChannelFactory;
+
+namespace {
+
+constexpr long kClientTimeoutMs = 0;  // Don't wait for non-existent services.
+constexpr int kDumpBufferSize = 2 * 4096;  // Two pages.
+
+class ControlClient : public android::pdx::ClientBase<ControlClient> {
+ public:
+  explicit ControlClient(const std::string& service_path, long timeout_ms);
+
+  void Reload();
+  std::string Dump();
+
+ private:
+  friend BASE;
+
+  ControlClient(const ControlClient&) = delete;
+  void operator=(const ControlClient&) = delete;
+};
+
+bool option_verbose = false;
+
+static struct option long_options[] = {
+    {"reload", required_argument, 0, 0},
+    {"dump", required_argument, 0, 0},
+    {"verbose", no_argument, 0, 0},
+    {0, 0, 0, 0},
+};
+
+#define printf_verbose(fmt, ... /*args*/) \
+  do {                                    \
+    if (option_verbose)                   \
+      printf(fmt, ##__VA_ARGS__);         \
+  } while (0)
+
+void HexDump(const void* pointer, size_t length);
+
+ControlClient::ControlClient(const std::string& service_path, long timeout_ms)
+    : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {}
+
+void ControlClient::Reload() {
+  android::pdx::Transaction trans{*this};
+  auto status = trans.Send<void>(android::pdx::opcodes::REPORT_SYSPROP_CHANGE,
+                                 nullptr, 0, nullptr, 0);
+  if (!status) {
+    fprintf(stderr, "Failed to send reload: %s\n",
+            status.GetErrorMessage().c_str());
+  }
+}
+
+std::string ControlClient::Dump() {
+  android::pdx::Transaction trans{*this};
+  std::vector<char> buffer(kDumpBufferSize);
+  auto status = trans.Send<int>(android::pdx::opcodes::DUMP_STATE, nullptr, 0,
+                                buffer.data(), buffer.size());
+
+  printf_verbose("ControlClient::Dump: ret=%d\n", ReturnStatusOrError(status));
+
+  if (!status) {
+    fprintf(stderr, "Failed to send dump request: %s\n",
+            status.GetErrorMessage().c_str());
+    return "";
+  } else if (status.get() > static_cast<ssize_t>(buffer.capacity())) {
+    fprintf(stderr, "Service returned a larger size than requested: %d\n",
+            status.get());
+    return "";
+  }
+
+  if (option_verbose)
+    HexDump(buffer.data(), status.get());
+
+  return std::string(buffer.data(), status.get());
+}
+
+int Usage(const std::string& command_name) {
+  printf("Usage: %s [options]\n", command_name.c_str());
+  printf("\t--verbose                      : Use verbose messages.\n");
+  printf(
+      "\t--reload <all | service path>  : Ask service(s) to reload system "
+      "properties.\n");
+  printf("\t--dump <all | service path>    : Dump service(s) state.\n");
+  return -1;
+}
+
+typedef int (*CallbackType)(const char* path, const struct stat* sb,
+                            int type_flag, FTW* ftw_buffer);
+
+int ReloadCommandCallback(const char* path, const struct stat* sb,
+                          int type_flag, FTW* ftw_buffer);
+int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag,
+                        FTW* ftw_buffer);
+
+void CallOnAllFiles(CallbackType callback, const std::string& base_path) {
+  const int kMaxDepth = 32;
+  nftw(base_path.c_str(), callback, kMaxDepth, FTW_PHYS);
+}
+
+int ReloadCommand(const std::string& service_path) {
+  printf_verbose("ReloadCommand: service_path=%s\n", service_path.c_str());
+
+  if (service_path == "" || service_path == "all") {
+    CallOnAllFiles(ReloadCommandCallback,
+                   ClientChannelFactory::GetRootEndpointPath());
+    return 0;
+  } else {
+    auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+    if (!client) {
+      fprintf(stderr, "Failed to open service at \"%s\".\n",
+              service_path.c_str());
+      return -1;
+    }
+
+    client->Reload();
+    return 0;
+  }
+}
+
+int DumpCommand(const std::string& service_path) {
+  printf_verbose("DumpCommand: service_path=%s\n", service_path.c_str());
+
+  if (service_path == "" || service_path == "all") {
+    CallOnAllFiles(DumpCommandCallback,
+                   ClientChannelFactory::GetRootEndpointPath());
+    return 0;
+  } else {
+    auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+    if (!client) {
+      fprintf(stderr, "Failed to open service at \"%s\".\n",
+              service_path.c_str());
+      return -1;
+    }
+
+    std::string response = client->Dump();
+    if (!response.empty()) {
+      printf(
+          "--------------------------------------------------------------------"
+          "---\n");
+      printf("%s:\n", service_path.c_str());
+      printf("%s\n", response.c_str());
+    }
+    return 0;
+  }
+}
+
+int ReloadCommandCallback(const char* path, const struct stat*, int type_flag,
+                          FTW*) {
+  if (type_flag == FTW_F)
+    ReloadCommand(path);
+  return 0;
+}
+
+int DumpCommandCallback(const char* path, const struct stat*, int type_flag,
+                        FTW*) {
+  if (type_flag == FTW_F)
+    DumpCommand(path);
+  return 0;
+}
+
+void HexDump(const void* pointer, size_t length) {
+  uintptr_t address = reinterpret_cast<uintptr_t>(pointer);
+
+  for (size_t count = 0; count < length; count += 16, address += 16) {
+    printf("0x%08lx: ", static_cast<unsigned long>(address));
+
+    for (size_t i = 0; i < 16u; i++) {
+      if (i < std::min(length - count, static_cast<size_t>(16))) {
+        printf("%02x ", *reinterpret_cast<const uint8_t*>(address + i));
+      } else {
+        printf("   ");
+      }
+    }
+
+    printf("|");
+
+    for (size_t i = 0; i < 16u; i++) {
+      if (i < std::min(length - count, static_cast<size_t>(16))) {
+        char c = *reinterpret_cast<const char*>(address + i);
+        if (isalnum(c) || c == ' ') {
+          printf("%c", c);
+        } else {
+          printf(".");
+        }
+      } else {
+        printf(" ");
+      }
+    }
+
+    printf("|\n");
+  }
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  int getopt_code;
+  int option_index;
+  std::string option = "";
+  std::string command = "";
+  std::string command_argument = "";
+
+  // Process command line options.
+  while ((getopt_code =
+              getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+    option = long_options[option_index].name;
+    printf_verbose("option=%s\n", option.c_str());
+    switch (getopt_code) {
+      case 0:
+        if (option == "verbose") {
+          option_verbose = true;
+        } else {
+          command = option;
+          if (optarg)
+            command_argument = optarg;
+        }
+        break;
+    }
+  }
+
+  printf_verbose("command=%s command_argument=%s\n", command.c_str(),
+                 command_argument.c_str());
+
+  if (command == "") {
+    return Usage(argv[0]);
+  } else if (command == "reload") {
+    return ReloadCommand(command_argument);
+  } else if (command == "dump") {
+    return DumpCommand(command_argument);
+  } else {
+    return Usage(argv[0]);
+  }
+}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
new file mode 100644
index 0000000..82a5ea7
--- /dev/null
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -0,0 +1,57 @@
+cc_library_static {
+    name: "libpdx_uds",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-DLOG_TAG=\"libpdx_uds\"",
+        "-DTRACE=0",
+    ],
+    export_include_dirs: ["private"],
+    local_include_dirs: ["private"],
+    srcs: [
+        "channel_event_set.cpp",
+        "channel_manager.cpp",
+        "client_channel_factory.cpp",
+        "client_channel.cpp",
+        "ipc_helper.cpp",
+        "service_dispatcher.cpp",
+        "service_endpoint.cpp",
+    ],
+    static_libs: [
+        "libcutils",
+        "libbase",
+        "libpdx",
+    ],
+    whole_static_libs: [
+        "libselinux",
+    ],
+}
+
+cc_test {
+    name: "libpdx_uds_tests",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "client_channel_tests.cpp",
+        "ipc_helper_tests.cpp",
+        "remote_method_tests.cpp",
+        "service_framework_tests.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libpdx_uds",
+        "libpdx",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp
new file mode 100644
index 0000000..ac4dea9
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_event_set.cpp
@@ -0,0 +1,115 @@
+#include "private/uds/channel_event_set.h"
+
+#include <log/log.h>
+
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelEventSet::ChannelEventSet() {
+  const int flags = EFD_CLOEXEC | EFD_NONBLOCK;
+  LocalHandle epoll_fd, event_fd;
+
+  if (!SetupHandle(epoll_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",
+          strerror(error));
+    return ErrorStatus{error};
+  } else {
+    return {};
+  }
+}
+
+int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) {
+  ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x",
+           clear_mask, set_mask);
+  const int old_bits = event_bits_;
+  const int new_bits = (event_bits_ & ~clear_mask) | set_mask;
+  event_bits_ = new_bits;
+
+  // If anything changed clear the event and update the event mask.
+  if (old_bits != new_bits) {
+    eventfd_t value;
+    eventfd_read(event_fd_.Get(), &value);
+
+    epoll_event event;
+    event.events = POLLIN;
+    event.data.u32 = event_bits_;
+    if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) <
+        0) {
+      const int error = errno;
+      ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s",
+            strerror(error));
+      return -error;
+    }
+  }
+
+  // If there are any bits set, re-trigger the eventfd.
+  if (new_bits)
+    eventfd_write(event_fd_.Get(), 1);
+
+  return 0;
+}
+
+Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle,
+                                          const char* error_name) {
+  const int error = errno;
+  handle->Reset(fd);
+  if (!*handle) {
+    ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s",
+          error_name, strerror(error));
+    return ErrorStatus{error};
+  }
+  return {};
+}
+
+Status<int> ChannelEventReceiver::GetPendingEvents() const {
+  constexpr long kTimeoutMs = 0;
+  epoll_event event;
+  const int count =
+      RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs));
+
+  Status<int> status;
+  if (count < 0) {
+    status.SetError(errno);
+    ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s",
+          status.GetErrorMessage().c_str());
+    return status;
+  }
+
+  const int mask_out = event.data.u32;
+  ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x",
+           mask_out);
+
+  status.SetValue(mask_out);
+  return status;
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp
new file mode 100644
index 0000000..afc0a4f
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_manager.cpp
@@ -0,0 +1,44 @@
+#include <uds/channel_manager.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelManager& ChannelManager::Get() {
+  static ChannelManager instance;
+  return instance;
+}
+
+void ChannelManager::CloseHandle(int32_t handle) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  auto channel = channels_.find(handle);
+  if (channel == channels_.end()) {
+    ALOGE("Invalid channel handle: %d", handle);
+  } else {
+    channels_.erase(channel);
+  }
+}
+
+LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd,
+                                                LocalHandle event_fd) {
+  if (data_fd && 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)});
+    return LocalChannelHandle(this, handle);
+  }
+  return LocalChannelHandle(nullptr, -1);
+}
+
+ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  auto channel = channels_.find(handle);
+  return channel != channels_.end() ? &channel->second : nullptr;
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
new file mode 100644
index 0000000..9d91617
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -0,0 +1,293 @@
+#include "uds/client_channel.h"
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <pdx/client.h>
+#include <pdx/service_endpoint.h>
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+struct TransactionState {
+  bool GetLocalFileHandle(int index, LocalHandle* handle) {
+    if (index < 0) {
+      handle->Reset(index);
+    } else if (static_cast<size_t>(index) < response.file_descriptors.size()) {
+      *handle = std::move(response.file_descriptors[index]);
+    } else {
+      return false;
+    }
+    return true;
+  }
+
+  bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+    if (index < 0) {
+      *handle = LocalChannelHandle{nullptr, index};
+    } 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));
+    } else {
+      return false;
+    }
+    return true;
+  }
+
+  FileReference PushFileHandle(BorrowedHandle handle) {
+    if (!handle)
+      return handle.Get();
+    request.file_descriptors.push_back(std::move(handle));
+    return request.file_descriptors.size() - 1;
+  }
+
+  ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
+    if (!handle)
+      return handle.value();
+
+    if (auto* channel_data =
+            ChannelManager::Get().GetChannelData(handle.value())) {
+      ChannelInfo<BorrowedHandle> channel_info;
+      channel_info.data_fd.Reset(handle.value());
+      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      request.channels.push_back(std::move(channel_info));
+      return request.channels.size() - 1;
+    } else {
+      return -1;
+    }
+  }
+
+  RequestHeader<BorrowedHandle> request;
+  ResponseHeader<LocalHandle> response;
+};
+
+Status<void> ReadAndDiscardData(const BorrowedHandle& socket_fd, size_t size) {
+  while (size > 0) {
+    // If there is more data to read in the message than the buffers provided
+    // by the caller, read and discard the extra data from the socket.
+    char buffer[1024];
+    size_t size_to_read = std::min(sizeof(buffer), size);
+    auto status = ReceiveData(socket_fd, buffer, size_to_read);
+    if (!status)
+      return status;
+    size -= size_to_read;
+  }
+  // We still want to return EIO error to the caller in case we had unexpected
+  // data in the socket stream.
+  return ErrorStatus(EIO);
+}
+
+Status<void> SendRequest(const BorrowedHandle& socket_fd,
+                         TransactionState* transaction_state, int opcode,
+                         const iovec* send_vector, size_t send_count,
+                         size_t max_recv_len) {
+  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;
+}
+
+Status<void> ReceiveResponse(const BorrowedHandle& socket_fd,
+                             TransactionState* transaction_state,
+                             const iovec* receive_vector, size_t receive_count,
+                             size_t max_recv_len) {
+  auto status = ReceiveData(socket_fd, &transaction_state->response);
+  if (!status)
+    return status;
+
+  if (transaction_state->response.recv_len > 0) {
+    std::vector<iovec> read_buffers;
+    size_t size_remaining = 0;
+    if (transaction_state->response.recv_len != max_recv_len) {
+      // If the receive buffer not exactly the size of data available, recreate
+      // the vector list to consume the data exactly since ReceiveDataVector()
+      // validates that the number of bytes received equals the number of bytes
+      // requested.
+      size_remaining = transaction_state->response.recv_len;
+      for (size_t i = 0; i < receive_count && size_remaining > 0; i++) {
+        read_buffers.push_back(receive_vector[i]);
+        iovec& last_vec = read_buffers.back();
+        if (last_vec.iov_len > size_remaining)
+          last_vec.iov_len = size_remaining;
+        size_remaining -= last_vec.iov_len;
+      }
+      receive_vector = read_buffers.data();
+      receive_count = read_buffers.size();
+    }
+    status = ReceiveDataVector(socket_fd, receive_vector, receive_count);
+    if (status && size_remaining > 0)
+      status = ReadAndDiscardData(socket_fd, size_remaining);
+  }
+  return status;
+}
+
+}  // anonymous namespace
+
+ClientChannel::ClientChannel(LocalChannelHandle channel_handle)
+    : channel_handle_{std::move(channel_handle)} {
+  channel_data_ = ChannelManager::Get().GetChannelData(channel_handle_.value());
+}
+
+std::unique_ptr<pdx::ClientChannel> ClientChannel::Create(
+    LocalChannelHandle channel_handle) {
+  return std::unique_ptr<pdx::ClientChannel>{
+      new ClientChannel{std::move(channel_handle)}};
+}
+
+ClientChannel::~ClientChannel() {
+  if (channel_handle_)
+    shutdown(channel_handle_.value(), SHUT_WR);
+}
+
+void* ClientChannel::AllocateTransactionState() { return new TransactionState; }
+
+void ClientChannel::FreeTransactionState(void* state) {
+  delete static_cast<TransactionState*>(state);
+}
+
+Status<void> ClientChannel::SendImpulse(int opcode, const void* buffer,
+                                        size_t length) {
+  std::unique_lock<std::mutex> lock(socket_mutex_);
+  Status<void> status;
+  android::pdx::uds::RequestHeader<BorrowedHandle> request;
+  if (length > request.impulse_payload.size() ||
+      (buffer == nullptr && length != 0)) {
+    status.SetError(EINVAL);
+    return status;
+  }
+
+  InitRequest(&request, opcode, length, 0, true);
+  memcpy(request.impulse_payload.data(), buffer, length);
+  return SendData(BorrowedHandle{channel_handle_.value()}, request);
+}
+
+Status<int> ClientChannel::SendAndReceive(void* transaction_state, int opcode,
+                                          const iovec* send_vector,
+                                          size_t send_count,
+                                          const iovec* receive_vector,
+                                          size_t receive_count) {
+  std::unique_lock<std::mutex> lock(socket_mutex_);
+  Status<int> result;
+  if ((send_vector == nullptr && send_count != 0) ||
+      (receive_vector == nullptr && receive_count != 0)) {
+    result.SetError(EINVAL);
+    return result;
+  }
+
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  size_t max_recv_len = CountVectorSize(receive_vector, receive_count);
+
+  auto status = SendRequest(BorrowedHandle{channel_handle_.value()}, state,
+                            opcode, send_vector, send_count, max_recv_len);
+  if (status) {
+    status = ReceiveResponse(BorrowedHandle{channel_handle_.value()}, state,
+                             receive_vector, receive_count, max_recv_len);
+  }
+  if (!result.PropagateError(status)) {
+    const int return_code = state->response.ret_code;
+    if (return_code >= 0)
+      result.SetValue(return_code);
+    else
+      result.SetError(-return_code);
+  }
+  return result;
+}
+
+Status<int> ClientChannel::SendWithInt(void* transaction_state, int opcode,
+                                       const iovec* send_vector,
+                                       size_t send_count,
+                                       const iovec* receive_vector,
+                                       size_t receive_count) {
+  return SendAndReceive(transaction_state, opcode, send_vector, send_count,
+                        receive_vector, receive_count);
+}
+
+Status<LocalHandle> ClientChannel::SendWithFileHandle(
+    void* transaction_state, int opcode, const iovec* send_vector,
+    size_t send_count, const iovec* receive_vector, size_t receive_count) {
+  Status<int> int_status =
+      SendAndReceive(transaction_state, opcode, send_vector, send_count,
+                     receive_vector, receive_count);
+  Status<LocalHandle> status;
+  if (status.PropagateError(int_status))
+    return status;
+
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  LocalHandle handle;
+  if (state->GetLocalFileHandle(int_status.get(), &handle)) {
+    status.SetValue(std::move(handle));
+  } else {
+    status.SetError(EINVAL);
+  }
+  return status;
+}
+
+Status<LocalChannelHandle> ClientChannel::SendWithChannelHandle(
+    void* transaction_state, int opcode, const iovec* send_vector,
+    size_t send_count, const iovec* receive_vector, size_t receive_count) {
+  Status<int> int_status =
+      SendAndReceive(transaction_state, opcode, send_vector, send_count,
+                     receive_vector, receive_count);
+  Status<LocalChannelHandle> status;
+  if (status.PropagateError(int_status))
+    return status;
+
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  LocalChannelHandle handle;
+  if (state->GetLocalChannelHandle(int_status.get(), &handle)) {
+    status.SetValue(std::move(handle));
+  } else {
+    status.SetError(EINVAL);
+  }
+  return status;
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+                                            const LocalHandle& handle) {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->PushFileHandle(handle.Borrow());
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+                                            const BorrowedHandle& handle) {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->PushFileHandle(handle.Duplicate());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+    void* transaction_state, const LocalChannelHandle& handle) {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->PushChannelHandle(handle.Borrow());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+    void* transaction_state, const BorrowedChannelHandle& handle) {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->PushChannelHandle(handle.Duplicate());
+}
+
+bool ClientChannel::GetFileHandle(void* transaction_state, FileReference ref,
+                                  LocalHandle* handle) const {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->GetLocalFileHandle(ref, handle);
+}
+
+bool ClientChannel::GetChannelHandle(void* transaction_state,
+                                     ChannelReference ref,
+                                     LocalChannelHandle* handle) const {
+  auto* state = static_cast<TransactionState*>(transaction_state);
+  return state->GetLocalChannelHandle(ref, handle);
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp
new file mode 100644
index 0000000..433f459
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -0,0 +1,160 @@
+#include <uds/client_channel_factory.h>
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+
+#include <uds/channel_manager.h>
+#include <uds/client_channel.h>
+#include <uds/ipc_helper.h>
+
+using std::chrono::duration_cast;
+using std::chrono::steady_clock;
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::string ClientChannelFactory::GetRootEndpointPath() {
+  return "/dev/socket/pdx";
+}
+
+std::string ClientChannelFactory::GetEndpointPath(
+    const std::string& endpoint_path) {
+  std::string path;
+  if (!endpoint_path.empty()) {
+    if (endpoint_path.front() == '/')
+      path = endpoint_path;
+    else
+      path = GetRootEndpointPath() + '/' + endpoint_path;
+  }
+  return path;
+}
+
+ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path)
+    : endpoint_path_{GetEndpointPath(endpoint_path)} {}
+
+ClientChannelFactory::ClientChannelFactory(LocalHandle socket)
+    : socket_{std::move(socket)} {}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+    const std::string& endpoint_path) {
+  return std::unique_ptr<pdx::ClientChannelFactory>{
+      new ClientChannelFactory{endpoint_path}};
+}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+    LocalHandle socket) {
+  return std::unique_ptr<pdx::ClientChannelFactory>{
+      new ClientChannelFactory{std::move(socket)}};
+}
+
+Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
+    int64_t timeout_ms) const {
+  Status<void> status;
+
+  bool connected = socket_.IsValid();
+  if (!connected) {
+    socket_.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+    LOG_ALWAYS_FATAL_IF(
+        endpoint_path_.empty(),
+        "ClientChannelFactory::Connect: unspecified socket path");
+  }
+
+  if (!socket_) {
+    ALOGE("ClientChannelFactory::Connect: socket error: %s", strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  bool use_timeout = (timeout_ms >= 0);
+  auto now = steady_clock::now();
+  auto time_end = now + std::chrono::milliseconds{timeout_ms};
+
+  int max_eaccess = 5;  // Max number of times to retry when EACCES returned.
+  while (!connected) {
+    int64_t timeout = -1;
+    if (use_timeout) {
+      auto remaining = time_end - now;
+      timeout = duration_cast<std::chrono::milliseconds>(remaining).count();
+      if (timeout < 0)
+        return ErrorStatus(ETIMEDOUT);
+    }
+    sockaddr_un remote;
+    remote.sun_family = AF_UNIX;
+    strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path));
+    remote.sun_path[sizeof(remote.sun_path) - 1] = '\0';
+    ALOGD("ClientChannelFactory: Waiting for endpoint at %s", remote.sun_path);
+    status = WaitForEndpoint(endpoint_path_, timeout);
+    if (!status)
+      return ErrorStatus(status.error());
+
+    ALOGD("ClientChannelFactory: Connecting to %s", remote.sun_path);
+    int ret = RETRY_EINTR(connect(
+        socket_.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
+    if (ret == -1) {
+      ALOGD("ClientChannelFactory: Connect error %d: %s", errno,
+            strerror(errno));
+      // if |max_eaccess| below reaches zero when errno is EACCES, the control
+      // flows into the next "else if" statement and a permanent error is
+      // returned from this function.
+      if (errno == ECONNREFUSED || (errno == EACCES && max_eaccess-- > 0)) {
+        // Connection refused/Permission denied can be the result of connecting
+        // too early (the service socket is created but its access rights are
+        // not set or not being listened to yet).
+        ALOGD("ClientChannelFactory: %s, waiting...", strerror(errno));
+        using namespace std::literals::chrono_literals;
+        std::this_thread::sleep_for(100ms);
+      } else if (errno != ENOENT && errno != ENOTDIR) {
+        // ENOENT/ENOTDIR might mean that the socket file/directory containing
+        // it has been just deleted. Try to wait for its creation and do not
+        // return an error immediately.
+        ALOGE(
+            "ClientChannelFactory::Connect: Failed to initialize connection "
+            "when connecting: %s",
+            strerror(errno));
+        return ErrorStatus(errno);
+      }
+    } else {
+      connected = true;
+      ALOGD("ClientChannelFactory: Connected successfully to %s...",
+            remote.sun_path);
+      ChannelConnectionInfo<LocalHandle> connection_info;
+      status = ReceiveData(socket_.Borrow(), &connection_info);
+      if (!status)
+        return status.error_status();
+      socket_ = std::move(connection_info.channel_fd);
+      if (!socket_) {
+        ALOGE("ClientChannelFactory::Connect: Failed to obtain channel socket");
+        return ErrorStatus(EIO);
+      }
+    }
+    if (use_timeout)
+      now = steady_clock::now();
+  }  // while (!connected)
+
+  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())
+    return ErrorStatus(EIO);
+
+  LocalHandle event_fd = std::move(response.file_descriptors[ref]);
+  return ClientChannel::Create(ChannelManager::Get().CreateHandle(
+      std::move(socket_), std::move(event_fd)));
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel_tests.cpp b/libs/vr/libpdx_uds/client_channel_tests.cpp
new file mode 100644
index 0000000..7c3c68a
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_tests.cpp
@@ -0,0 +1,162 @@
+#include <uds/client_channel.h>
+
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <limits>
+#include <random>
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <pdx/client.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/service.h>
+
+#include <uds/client_channel_factory.h>
+#include <uds/service_endpoint.h>
+
+using testing::Return;
+using testing::_;
+
+using android::pdx::ClientBase;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::uds::ClientChannel;
+using android::pdx::uds::ClientChannelFactory;
+using android::pdx::uds::Endpoint;
+
+namespace {
+
+struct TestProtocol {
+  using DataType = int8_t;
+  enum {
+    kOpSum = 0,
+  };
+  PDX_REMOTE_METHOD(Sum, kOpSum, int64_t(const std::vector<DataType>&));
+};
+
+class TestService : public ServiceBase<TestService> {
+ public:
+  TestService(std::unique_ptr<Endpoint> endpoint)
+      : ServiceBase{"TestService", std::move(endpoint)} {}
+
+  Status<void> HandleMessage(Message& message) override {
+    switch (message.GetOp()) {
+      case TestProtocol::kOpSum:
+        DispatchRemoteMethod<TestProtocol::Sum>(*this, &TestService::OnSum,
+                                                message);
+        return {};
+
+      default:
+        return Service::HandleMessage(message);
+    }
+  }
+
+  int64_t OnSum(Message& /*message*/,
+                const std::vector<TestProtocol::DataType>& data) {
+    return std::accumulate(data.begin(), data.end(), int64_t{0});
+  }
+};
+
+class TestClient : public ClientBase<TestClient> {
+ public:
+  using ClientBase::ClientBase;
+
+  int64_t Sum(const std::vector<TestProtocol::DataType>& data) {
+    auto status = InvokeRemoteMethod<TestProtocol::Sum>(data);
+    return status ? status.get() : -1;
+  }
+};
+
+class TestServiceRunner {
+ public:
+  TestServiceRunner(LocalHandle channel_socket) {
+    auto endpoint = Endpoint::CreateFromSocketFd(LocalHandle{});
+    endpoint->RegisterNewChannelForTests(std::move(channel_socket));
+    service_ = TestService::Create(std::move(endpoint));
+    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    dispatcher_->AddService(service_);
+    dispatch_thread_ = std::thread(
+        std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+  }
+
+  ~TestServiceRunner() {
+    dispatcher_->SetCanceled(true);
+    dispatch_thread_.join();
+    dispatcher_->RemoveService(service_);
+  }
+
+ private:
+  std::shared_ptr<TestService> service_;
+  std::unique_ptr<ServiceDispatcher> dispatcher_;
+  std::thread dispatch_thread_;
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+  void SetUp() override {
+    int channel_sockets[2] = {};
+    ASSERT_EQ(
+        0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_sockets));
+    LocalHandle service_channel{channel_sockets[0]};
+    LocalHandle client_channel{channel_sockets[1]};
+
+    service_runner_.reset(new TestServiceRunner{std::move(service_channel)});
+    auto factory = ClientChannelFactory::Create(std::move(client_channel));
+    auto status = factory->Connect(android::pdx::Client::kInfiniteTimeout);
+    ASSERT_TRUE(status);
+    client_ = TestClient::Create(status.take());
+  }
+
+  void TearDown() override {
+    service_runner_.reset();
+    client_.reset();
+  }
+
+ protected:
+  std::unique_ptr<TestServiceRunner> service_runner_;
+  std::shared_ptr<TestClient> client_;
+};
+
+TEST_F(ClientChannelTest, MultithreadedClient) {
+  constexpr int kNumTestThreads = 8;
+  constexpr size_t kDataSize = 1000;  // Try to keep RPC buffer size below 4K.
+
+  std::random_device rd;
+  std::mt19937 gen{rd()};
+  std::uniform_int_distribution<TestProtocol::DataType> dist{
+      std::numeric_limits<TestProtocol::DataType>::min(),
+      std::numeric_limits<TestProtocol::DataType>::max()};
+
+  auto worker = [](std::shared_ptr<TestClient> client,
+                   std::vector<TestProtocol::DataType> data) {
+    constexpr int kMaxIterations = 500;
+    int64_t expected = std::accumulate(data.begin(), data.end(), int64_t{0});
+    for (int i = 0; i < kMaxIterations; i++) {
+      ASSERT_EQ(expected, client->Sum(data));
+    }
+  };
+
+  // Start client threads.
+  std::vector<TestProtocol::DataType> data;
+  data.resize(kDataSize);
+  std::vector<std::thread> threads;
+  for (int i = 0; i < kNumTestThreads; i++) {
+    std::generate(data.begin(), data.end(),
+                  [&dist, &gen]() { return dist(gen); });
+    threads.emplace_back(worker, client_, data);
+  }
+
+  // Wait for threads to finish.
+  for (auto& thread : threads)
+    thread.join();
+}
+
+}  // namespace
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
new file mode 100644
index 0000000..d75ce86
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -0,0 +1,534 @@
+#include "uds/ipc_helper.h"
+
+#include <alloca.h>
+#include <errno.h>
+#include <log/log.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+
+#include <pdx/service.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+// Default implementations of Send/Receive interfaces to use standard socket
+// send/sendmsg/recv/recvmsg functions.
+class SocketSender : public SendInterface {
+ public:
+  ssize_t Send(int socket_fd, const void* data, size_t size,
+               int flags) override {
+    return send(socket_fd, data, size, flags);
+  }
+  ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) override {
+    return sendmsg(socket_fd, msg, flags);
+  }
+} g_socket_sender;
+
+class SocketReceiver : public RecvInterface {
+ public:
+  ssize_t Receive(int socket_fd, void* data, size_t size, int flags) override {
+    return recv(socket_fd, data, size, flags);
+  }
+  ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) override {
+    return recvmsg(socket_fd, msg, flags);
+  }
+} g_socket_receiver;
+
+}  // anonymous namespace
+
+// Helper wrappers around send()/sendmsg() which repeat send() calls on data
+// that was not sent with the initial call to send/sendmsg. This is important to
+// handle transmissions interrupted by signals.
+Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+                     const void* data, size_t size) {
+  Status<void> ret;
+  const uint8_t* ptr = static_cast<const uint8_t*>(data);
+  while (size > 0) {
+    ssize_t size_written =
+        RETRY_EINTR(sender->Send(socket_fd.Get(), ptr, size, MSG_NOSIGNAL));
+    if (size_written < 0) {
+      ret.SetError(errno);
+      ALOGE("SendAll: Failed to send data over socket: %s",
+            ret.GetErrorMessage().c_str());
+      break;
+    }
+    size -= size_written;
+    ptr += size_written;
+  }
+  return ret;
+}
+
+Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+                        const msghdr* msg) {
+  Status<void> ret;
+  ssize_t sent_size =
+      RETRY_EINTR(sender->SendMessage(socket_fd.Get(), msg, MSG_NOSIGNAL));
+  if (sent_size < 0) {
+    ret.SetError(errno);
+    ALOGE("SendMsgAll: Failed to send data over socket: %s",
+          ret.GetErrorMessage().c_str());
+    return ret;
+  }
+
+  ssize_t chunk_start_offset = 0;
+  for (size_t i = 0; i < msg->msg_iovlen; i++) {
+    ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len;
+    if (sent_size < chunk_end_offset) {
+      size_t offset_within_chunk = sent_size - chunk_start_offset;
+      size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk;
+      const uint8_t* chunk_base =
+          static_cast<const uint8_t*>(msg->msg_iov[i].iov_base);
+      ret = SendAll(sender, socket_fd, chunk_base + offset_within_chunk,
+                    data_size);
+      if (!ret)
+        break;
+      sent_size += data_size;
+    }
+    chunk_start_offset = chunk_end_offset;
+  }
+  return ret;
+}
+
+// Helper wrappers around recv()/recvmsg() which repeat recv() calls on data
+// that was not received with the initial call to recvmsg(). This is important
+// to handle transmissions interrupted by signals as well as the case when
+// initial data did not arrive in a single chunk over the socket (e.g. socket
+// buffer was full at the time of transmission, and only portion of initial
+// message was sent and the rest was blocked until the buffer was cleared by the
+// receiving side).
+Status<void> RecvMsgAll(RecvInterface* receiver,
+                        const BorrowedHandle& socket_fd, msghdr* msg) {
+  Status<void> ret;
+  ssize_t size_read = RETRY_EINTR(receiver->ReceiveMessage(
+      socket_fd.Get(), msg, MSG_WAITALL | MSG_CMSG_CLOEXEC));
+  if (size_read < 0) {
+    ret.SetError(errno);
+    ALOGE("RecvMsgAll: Failed to receive data from socket: %s",
+          ret.GetErrorMessage().c_str());
+    return ret;
+  } else if (size_read == 0) {
+    ret.SetError(ESHUTDOWN);
+    ALOGW("RecvMsgAll: Socket has been shut down");
+    return ret;
+  }
+
+  ssize_t chunk_start_offset = 0;
+  for (size_t i = 0; i < msg->msg_iovlen; i++) {
+    ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len;
+    if (size_read < chunk_end_offset) {
+      size_t offset_within_chunk = size_read - chunk_start_offset;
+      size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk;
+      uint8_t* chunk_base = static_cast<uint8_t*>(msg->msg_iov[i].iov_base);
+      ret = RecvAll(receiver, socket_fd, chunk_base + offset_within_chunk,
+                    data_size);
+      if (!ret)
+        break;
+      size_read += data_size;
+    }
+    chunk_start_offset = chunk_end_offset;
+  }
+  return ret;
+}
+
+Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd,
+                     void* data, size_t size) {
+  Status<void> ret;
+  uint8_t* ptr = static_cast<uint8_t*>(data);
+  while (size > 0) {
+    ssize_t size_read = RETRY_EINTR(receiver->Receive(
+        socket_fd.Get(), ptr, size, MSG_WAITALL | MSG_CMSG_CLOEXEC));
+    if (size_read < 0) {
+      ret.SetError(errno);
+      ALOGE("RecvAll: Failed to receive data from socket: %s",
+            ret.GetErrorMessage().c_str());
+      break;
+    } else if (size_read == 0) {
+      ret.SetError(ESHUTDOWN);
+      ALOGW("RecvAll: Socket has been shut down");
+      break;
+    }
+    size -= size_read;
+    ptr += size_read;
+  }
+  return ret;
+}
+
+uint32_t kMagicPreamble = 0x7564736d;  // 'udsm'.
+
+struct MessagePreamble {
+  uint32_t magic{0};
+  uint32_t data_size{0};
+  uint32_t fd_count{0};
+};
+
+Status<void> SendPayload::Send(const BorrowedHandle& socket_fd) {
+  return Send(socket_fd, nullptr);
+}
+
+Status<void> SendPayload::Send(const BorrowedHandle& socket_fd,
+                               const ucred* cred) {
+  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;
+
+  if (cred || !file_handles_.empty()) {
+    const size_t fd_bytes = file_handles_.size() * sizeof(int);
+    msg.msg_controllen = (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
+                         (fd_bytes == 0 ? 0 : CMSG_SPACE(fd_bytes));
+    msg.msg_control = alloca(msg.msg_controllen);
+
+    cmsghdr* control = CMSG_FIRSTHDR(&msg);
+    if (cred) {
+      control->cmsg_level = SOL_SOCKET;
+      control->cmsg_type = SCM_CREDENTIALS;
+      control->cmsg_len = CMSG_LEN(sizeof(ucred));
+      memcpy(CMSG_DATA(control), cred, sizeof(ucred));
+      control = CMSG_NXTHDR(&msg, control);
+    }
+
+    if (fd_bytes) {
+      control->cmsg_level = SOL_SOCKET;
+      control->cmsg_type = SCM_RIGHTS;
+      control->cmsg_len = CMSG_LEN(fd_bytes);
+      memcpy(CMSG_DATA(control), file_handles_.data(), fd_bytes);
+    }
+  }
+
+  return SendMsgAll(sender, socket_fd, &msg);
+}
+
+// MessageWriter
+void* SendPayload::GetNextWriteBufferSection(size_t size) {
+  return buffer_.grow_by(size);
+}
+
+OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; }
+
+// OutputResourceMapper
+Status<FileReference> SendPayload::PushFileHandle(const LocalHandle& handle) {
+  if (handle) {
+    const int ref = file_handles_.size();
+    file_handles_.push_back(handle.Get());
+    return ref;
+  } else {
+    return handle.Get();
+  }
+}
+
+Status<FileReference> SendPayload::PushFileHandle(
+    const BorrowedHandle& handle) {
+  if (handle) {
+    const int ref = file_handles_.size();
+    file_handles_.push_back(handle.Get());
+    return ref;
+  } else {
+    return handle.Get();
+  }
+}
+
+Status<FileReference> SendPayload::PushFileHandle(const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+Status<ChannelReference> SendPayload::PushChannelHandle(
+    const LocalChannelHandle& /*handle*/) {
+  return ErrorStatus{EOPNOTSUPP};
+}
+Status<ChannelReference> SendPayload::PushChannelHandle(
+    const BorrowedChannelHandle& /*handle*/) {
+  return ErrorStatus{EOPNOTSUPP};
+}
+Status<ChannelReference> SendPayload::PushChannelHandle(
+    const RemoteChannelHandle& /*handle*/) {
+  return ErrorStatus{EOPNOTSUPP};
+}
+
+Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd) {
+  return Receive(socket_fd, nullptr);
+}
+
+Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd,
+                                     ucred* cred) {
+  RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver;
+  MessagePreamble preamble;
+  Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble));
+  if (!ret)
+    return ret;
+
+  if (preamble.magic != kMagicPreamble) {
+    ALOGE("ReceivePayload::Receive: Message header is invalid");
+    ret.SetError(EIO);
+    return ret;
+  }
+
+  buffer_.resize(preamble.data_size);
+  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);
+  while (cmsg) {
+    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS &&
+        cred && cmsg->cmsg_len == CMSG_LEN(sizeof(ucred))) {
+      cred_available = true;
+      memcpy(cred, CMSG_DATA(cmsg), sizeof(ucred));
+    } else if (cmsg->cmsg_level == SOL_SOCKET &&
+               cmsg->cmsg_type == SCM_RIGHTS) {
+      socklen_t payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+      const int* fds = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+      size_t fd_count = payload_len / sizeof(int);
+      std::transform(fds, fds + fd_count, std::back_inserter(file_handles_),
+                     [](int fd) { return LocalHandle{fd}; });
+    }
+    cmsg = CMSG_NXTHDR(&msg, cmsg);
+  }
+
+  if (cred && !cred_available) {
+    ALOGE("ReceivePayload::Receive: Failed to obtain message credentials");
+    ret.SetError(EIO);
+  }
+
+  return ret;
+}
+
+// MessageReader
+MessageReader::BufferSection ReceivePayload::GetNextReadBufferSection() {
+  return {buffer_.data() + read_pos_, &*buffer_.end()};
+}
+
+void ReceivePayload::ConsumeReadBufferSectionData(const void* new_start) {
+  read_pos_ = PointerDistance(new_start, buffer_.data());
+}
+
+InputResourceMapper* ReceivePayload::GetInputResourceMapper() { return this; }
+
+// InputResourceMapper
+bool ReceivePayload::GetFileHandle(FileReference ref, LocalHandle* handle) {
+  if (ref < 0) {
+    *handle = LocalHandle{ref};
+    return true;
+  }
+  if (static_cast<size_t>(ref) > file_handles_.size())
+    return false;
+  *handle = std::move(file_handles_[ref]);
+  return true;
+}
+
+bool ReceivePayload::GetChannelHandle(ChannelReference /*ref*/,
+                                      LocalChannelHandle* /*handle*/) {
+  return false;
+}
+
+Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
+                      size_t size) {
+  return SendAll(&g_socket_sender, socket_fd, data, size);
+}
+
+Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data,
+                            size_t count) {
+  msghdr msg = {};
+  msg.msg_iov = const_cast<iovec*>(data);
+  msg.msg_iovlen = count;
+  return SendMsgAll(&g_socket_sender, socket_fd, &msg);
+}
+
+Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data,
+                         size_t size) {
+  return RecvAll(&g_socket_receiver, socket_fd, data, size);
+}
+
+Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd,
+                               const iovec* data, size_t count) {
+  msghdr msg = {};
+  msg.msg_iov = const_cast<iovec*>(data);
+  msg.msg_iovlen = count;
+  return RecvMsgAll(&g_socket_receiver, socket_fd, &msg);
+}
+
+size_t CountVectorSize(const iovec* vector, size_t count) {
+  return std::accumulate(
+      vector, vector + count, size_t{0},
+      [](size_t size, const iovec& vec) { return size + vec.iov_len; });
+}
+
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+                 int opcode, uint32_t send_len, uint32_t max_recv_len,
+                 bool is_impulse) {
+  request->op = opcode;
+  request->cred.pid = getpid();
+  request->cred.uid = geteuid();
+  request->cred.gid = getegid();
+  request->send_len = send_len;
+  request->max_recv_len = max_recv_len;
+  request->is_impulse = is_impulse;
+}
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+                             int64_t timeout_ms) {
+  // Endpoint path must be absolute.
+  if (endpoint_path.empty() || endpoint_path.front() != '/')
+    return ErrorStatus(EINVAL);
+
+  // Create inotify fd.
+  LocalHandle fd{inotify_init()};
+  if (!fd)
+    return ErrorStatus(errno);
+
+  // Set the inotify fd to non-blocking.
+  int ret = fcntl(fd.Get(), F_GETFL);
+  fcntl(fd.Get(), F_SETFL, ret | O_NONBLOCK);
+
+  // Setup the pollfd.
+  pollfd pfd = {fd.Get(), POLLIN, 0};
+
+  // Find locations of each path separator.
+  std::vector<size_t> separators{0};  // The path is absolute, so '/' is at #0.
+  size_t pos = endpoint_path.find('/', 1);
+  while (pos != std::string::npos) {
+    separators.push_back(pos);
+    pos = endpoint_path.find('/', pos + 1);
+  }
+  separators.push_back(endpoint_path.size());
+
+  // Walk down the path, checking for existence and waiting if needed.
+  pos = 1;
+  size_t links = 0;
+  std::string current;
+  while (pos < separators.size() && links <= MAXSYMLINKS) {
+    std::string previous = current;
+    current = endpoint_path.substr(0, separators[pos]);
+
+    // Check for existence; proceed to setup a watch if not.
+    if (access(current.c_str(), F_OK) < 0) {
+      if (errno != ENOENT)
+        return ErrorStatus(errno);
+
+      // Extract the name of the path component to wait for.
+      std::string next = current.substr(
+          separators[pos - 1] + 1, separators[pos] - separators[pos - 1] - 1);
+
+      // Add a watch on the last existing directory we reach.
+      int wd = inotify_add_watch(
+          fd.Get(), previous.c_str(),
+          IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO);
+      if (wd < 0) {
+        if (errno != ENOENT)
+          return ErrorStatus(errno);
+        // Restart at the beginning if previous was deleted.
+        links = 0;
+        current.clear();
+        pos = 1;
+        continue;
+      }
+
+      // Make sure current didn't get created before the watch was added.
+      ret = access(current.c_str(), F_OK);
+      if (ret < 0) {
+        if (errno != ENOENT)
+          return ErrorStatus(errno);
+
+        bool exit_poll = false;
+        while (!exit_poll) {
+          // Wait for an event or timeout.
+          ret = poll(&pfd, 1, timeout_ms);
+          if (ret <= 0)
+            return ErrorStatus(ret == 0 ? ETIMEDOUT : errno);
+
+          // Read events.
+          char buffer[sizeof(inotify_event) + NAME_MAX + 1];
+
+          ret = read(fd.Get(), buffer, sizeof(buffer));
+          if (ret < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+              continue;
+            else
+              return ErrorStatus(errno);
+          } else if (static_cast<size_t>(ret) < sizeof(struct inotify_event)) {
+            return ErrorStatus(EIO);
+          }
+
+          auto* event = reinterpret_cast<const inotify_event*>(buffer);
+          auto* end = reinterpret_cast<const inotify_event*>(buffer + ret);
+          while (event < end) {
+            std::string event_for;
+            if (event->len > 0)
+              event_for = event->name;
+
+            if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+              // See if this is the droid we're looking for.
+              if (next == event_for) {
+                exit_poll = true;
+                break;
+              }
+            } else if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
+              // Restart at the beginning if our watch dir is deleted.
+              links = 0;
+              current.clear();
+              pos = 0;
+              exit_poll = true;
+              break;
+            }
+
+            event = reinterpret_cast<const inotify_event*>(AdvancePointer(
+                event, sizeof(struct inotify_event) + event->len));
+          }  // while (event < end)
+        }    // while (!exit_poll)
+      }      // Current dir doesn't exist.
+      ret = inotify_rm_watch(fd.Get(), wd);
+      if (ret < 0 && errno != EINVAL)
+        return ErrorStatus(errno);
+    }  // if (access(current.c_str(), F_OK) < 0)
+
+    // Check for symbolic link and update link count.
+    struct stat stat_buf;
+    ret = lstat(current.c_str(), &stat_buf);
+    if (ret < 0 && errno != ENOENT)
+      return ErrorStatus(errno);
+    else if (ret == 0 && S_ISLNK(stat_buf.st_mode))
+      links++;
+    pos++;
+  }  // while (pos < separators.size() && links <= MAXSYMLINKS)
+
+  return {};
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/ipc_helper_tests.cpp b/libs/vr/libpdx_uds/ipc_helper_tests.cpp
new file mode 100644
index 0000000..bfa827e
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper_tests.cpp
@@ -0,0 +1,365 @@
+#include "uds/ipc_helper.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::Return;
+using testing::SetErrnoAndReturn;
+using testing::_;
+
+using android::pdx::BorrowedHandle;
+using android::pdx::uds::SendInterface;
+using android::pdx::uds::RecvInterface;
+using android::pdx::uds::SendAll;
+using android::pdx::uds::SendMsgAll;
+using android::pdx::uds::RecvAll;
+using android::pdx::uds::RecvMsgAll;
+
+namespace {
+
+// Useful constants for tests.
+static constexpr intptr_t kPtr = 1234;
+static constexpr int kSocketFd = 5678;
+static const BorrowedHandle kSocket{kSocketFd};
+
+// Helper functions to construct test data pointer values.
+void* IntToPtr(intptr_t value) { return reinterpret_cast<void*>(value); }
+const void* IntToConstPtr(intptr_t value) {
+  return reinterpret_cast<const void*>(value);
+}
+
+// Mock classes for SendInterface/RecvInterface.
+class MockSender : public SendInterface {
+ public:
+  MOCK_METHOD4(Send, ssize_t(int socket_fd, const void* data, size_t size,
+                             int flags));
+  MOCK_METHOD3(SendMessage,
+               ssize_t(int socket_fd, const msghdr* msg, int flags));
+};
+
+class MockReceiver : public RecvInterface {
+ public:
+  MOCK_METHOD4(Receive,
+               ssize_t(int socket_fd, void* data, size_t size, int flags));
+  MOCK_METHOD3(ReceiveMessage, ssize_t(int socket_fd, msghdr* msg, int flags));
+};
+
+// Test case classes.
+class SendTest : public testing::Test {
+ public:
+  SendTest() {
+    ON_CALL(sender_, Send(_, _, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+    ON_CALL(sender_, SendMessage(_, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+  }
+
+ protected:
+  MockSender sender_;
+};
+
+class RecvTest : public testing::Test {
+ public:
+  RecvTest() {
+    ON_CALL(receiver_, Receive(_, _, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+    ON_CALL(receiver_, ReceiveMessage(_, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+  }
+
+ protected:
+  MockReceiver receiver_;
+};
+
+class MessageTestBase : public testing::Test {
+ public:
+  MessageTestBase() {
+    memset(&msg_, 0, sizeof(msg_));
+    msg_.msg_iovlen = data_.size();
+    msg_.msg_iov = data_.data();
+  }
+
+ protected:
+  static constexpr intptr_t kPtr1 = kPtr;
+  static constexpr intptr_t kPtr2 = kPtr + 200;
+  static constexpr intptr_t kPtr3 = kPtr + 1000;
+
+  MockSender sender_;
+  msghdr msg_;
+  std::vector<iovec> data_{
+      {IntToPtr(kPtr1), 100}, {IntToPtr(kPtr2), 200}, {IntToPtr(kPtr3), 300}};
+};
+
+class SendMessageTest : public MessageTestBase {
+ public:
+  SendMessageTest() {
+    ON_CALL(sender_, Send(_, _, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+    ON_CALL(sender_, SendMessage(_, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+  }
+
+ protected:
+  MockSender sender_;
+};
+
+class RecvMessageTest : public MessageTestBase {
+ public:
+  RecvMessageTest() {
+    ON_CALL(receiver_, Receive(_, _, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+    ON_CALL(receiver_, ReceiveMessage(_, _, _))
+        .WillByDefault(SetErrnoAndReturn(EIO, -1));
+  }
+
+ protected:
+  MockReceiver receiver_;
+};
+
+// Actual tests.
+
+// SendAll
+TEST_F(SendTest, Complete) {
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+      .WillOnce(Return(100));
+
+  auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Signal) {
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+      .WillOnce(Return(20));
+  EXPECT_CALL(sender_,
+              Send(kSocketFd, IntToConstPtr(kPtr + 20), 80, MSG_NOSIGNAL))
+      .WillOnce(Return(40));
+  EXPECT_CALL(sender_,
+              Send(kSocketFd, IntToConstPtr(kPtr + 60), 40, MSG_NOSIGNAL))
+      .WillOnce(Return(40));
+
+  auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Eintr) {
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(100));
+
+  auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Error) {
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+      .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+  auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(SendTest, Error2) {
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+      .WillOnce(Return(50));
+  EXPECT_CALL(sender_,
+              Send(kSocketFd, IntToConstPtr(kPtr + 50), 50, MSG_NOSIGNAL))
+      .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+  auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+// RecvAll
+TEST_F(RecvTest, Complete) {
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100,
+                                 MSG_WAITALL | MSG_CMSG_CLOEXEC))
+      .WillOnce(Return(100));
+
+  auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Signal) {
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+      .WillOnce(Return(20));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 20), 80, _))
+      .WillOnce(Return(40));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 60), 40, _))
+      .WillOnce(Return(40));
+
+  auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Eintr) {
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(100));
+
+  auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Error) {
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+      .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+  auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(RecvTest, Error2) {
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+      .WillOnce(Return(30));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 30), 70, _))
+      .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+  auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EIO, status.error());
+}
+
+// SendMsgAll
+TEST_F(SendMessageTest, Complete) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, MSG_NOSIGNAL))
+      .WillOnce(Return(600));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Partial) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(70));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _))
+      .WillOnce(Return(30));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _))
+      .WillOnce(Return(190));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2 + 190), 10, _))
+      .WillOnce(Return(10));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _))
+      .WillOnce(Return(300));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Partial2) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(310));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3 + 10), 290, _))
+      .WillOnce(Return(290));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Eintr) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(70));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(30));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _))
+      .WillOnce(Return(200));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _))
+      .WillOnce(Return(300));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Error) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _))
+      .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EBADF, status.error());
+}
+
+TEST_F(SendMessageTest, Error2) {
+  EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(20));
+  EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 20), 80, _))
+      .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+  auto status = SendMsgAll(&sender_, kSocket, &msg_);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EBADF, status.error());
+}
+
+// RecvMsgAll
+TEST_F(RecvMessageTest, Complete) {
+  EXPECT_CALL(receiver_,
+              ReceiveMessage(kSocketFd, &msg_, MSG_WAITALL | MSG_CMSG_CLOEXEC))
+      .WillOnce(Return(600));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Partial) {
+  EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+      .WillOnce(Return(70));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _))
+      .WillOnce(Return(30));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _))
+      .WillOnce(Return(190));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2 + 190), 10, _))
+      .WillOnce(Return(10));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _))
+      .WillOnce(Return(300));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Partial2) {
+  EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+      .WillOnce(Return(310));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3 + 10), 290, _))
+      .WillOnce(Return(290));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Eintr) {
+  EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(70));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _))
+      .WillOnce(SetErrnoAndReturn(EINTR, -1))
+      .WillOnce(Return(30));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _))
+      .WillOnce(Return(200));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _))
+      .WillOnce(Return(300));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Error) {
+  EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+      .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EBADF, status.error());
+}
+
+TEST_F(RecvMessageTest, Error2) {
+  EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+      .WillOnce(Return(20));
+  EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 20), 80, _))
+      .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+  auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+  ASSERT_FALSE(status);
+  EXPECT_EQ(EBADF, status.error());
+}
+
+}  // namespace
diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
new file mode 100644
index 0000000..1f464d5
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
@@ -0,0 +1,62 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+#define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+
+#include <errno.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelEventSet {
+ public:
+  ChannelEventSet();
+  ChannelEventSet(ChannelEventSet&&) = default;
+  ChannelEventSet& operator=(ChannelEventSet&&) = default;
+
+  BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+
+  explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; }
+
+  Status<void> AddDataFd(const LocalHandle& data_fd);
+  int ModifyEvents(int clear_mask, int set_mask);
+
+ private:
+  LocalHandle epoll_fd_;
+  LocalHandle event_fd_;
+  uint32_t event_bits_ = 0;
+
+  static Status<void> SetupHandle(int fd, LocalHandle* handle,
+                                  const char* error_name);
+
+  ChannelEventSet(const ChannelEventSet&) = delete;
+  void operator=(const ChannelEventSet&) = delete;
+};
+
+class ChannelEventReceiver {
+ public:
+  ChannelEventReceiver() = default;
+  ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {}
+  ChannelEventReceiver(ChannelEventReceiver&&) = default;
+  ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default;
+
+  BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+  Status<int> GetPendingEvents() const;
+
+ private:
+  LocalHandle epoll_fd_;
+
+  ChannelEventReceiver(const ChannelEventReceiver&) = delete;
+  void operator=(const ChannelEventReceiver&) = delete;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h
new file mode 100644
index 0000000..2aca414
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h
@@ -0,0 +1,40 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+
+#include <mutex>
+#include <unordered_map>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <uds/channel_event_set.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelManager : public ChannelManagerInterface {
+ public:
+  static ChannelManager& Get();
+
+  LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd);
+  struct ChannelData {
+    LocalHandle data_fd;
+    ChannelEventReceiver event_receiver;
+  };
+
+  ChannelData* GetChannelData(int32_t handle);
+
+ private:
+  ChannelManager() = default;
+
+  void CloseHandle(int32_t handle) override;
+
+  std::mutex mutex_;
+  std::unordered_map<int32_t, ChannelData> channels_;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
new file mode 100644
index 0000000..8f607f5
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -0,0 +1,85 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+
+#include <pdx/client_channel.h>
+
+#include <mutex>
+
+#include <uds/channel_event_set.h>
+#include <uds/channel_manager.h>
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannel : public pdx::ClientChannel {
+ public:
+  ~ClientChannel() override;
+
+  static std::unique_ptr<pdx::ClientChannel> Create(
+      LocalChannelHandle channel_handle);
+
+  uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; }
+
+  int event_fd() const override {
+    return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1;
+  }
+  Status<int> GetEventMask(int /*events*/) override {
+    if (channel_data_)
+      return channel_data_->event_receiver.GetPendingEvents();
+    else
+      return ErrorStatus(EINVAL);
+  }
+
+  LocalChannelHandle& GetChannelHandle() override { return channel_handle_; }
+  void* AllocateTransactionState() override;
+  void FreeTransactionState(void* state) override;
+
+  Status<void> SendImpulse(int opcode, const void* buffer,
+                           size_t length) override;
+
+  Status<int> SendWithInt(void* transaction_state, int opcode,
+                          const iovec* send_vector, size_t send_count,
+                          const iovec* receive_vector,
+                          size_t receive_count) override;
+  Status<LocalHandle> SendWithFileHandle(void* transaction_state, int opcode,
+                                         const iovec* send_vector,
+                                         size_t send_count,
+                                         const iovec* receive_vector,
+                                         size_t receive_count) override;
+  Status<LocalChannelHandle> SendWithChannelHandle(
+      void* transaction_state, int opcode, const iovec* send_vector,
+      size_t send_count, const iovec* receive_vector,
+      size_t receive_count) override;
+
+  FileReference PushFileHandle(void* transaction_state,
+                               const LocalHandle& handle) override;
+  FileReference PushFileHandle(void* transaction_state,
+                               const BorrowedHandle& handle) override;
+  ChannelReference PushChannelHandle(void* transaction_state,
+                                     const LocalChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      void* transaction_state, const BorrowedChannelHandle& handle) override;
+  bool GetFileHandle(void* transaction_state, FileReference ref,
+                     LocalHandle* handle) const override;
+  bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+                        LocalChannelHandle* handle) const override;
+
+ private:
+  explicit ClientChannel(LocalChannelHandle channel_handle);
+
+  Status<int> SendAndReceive(void* transaction_state, int opcode,
+                             const iovec* send_vector, size_t send_count,
+                             const iovec* receive_vector, size_t receive_count);
+
+  LocalChannelHandle channel_handle_;
+  ChannelManager::ChannelData* channel_data_;
+  std::mutex socket_mutex_;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel_factory.h b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
new file mode 100644
index 0000000..c43c5c7
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <string>
+
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannelFactory : public pdx::ClientChannelFactory {
+ public:
+  static std::unique_ptr<pdx::ClientChannelFactory> Create(
+      const std::string& endpoint_path);
+  static std::unique_ptr<pdx::ClientChannelFactory> Create(LocalHandle socket);
+
+  Status<std::unique_ptr<pdx::ClientChannel>> Connect(
+      int64_t timeout_ms) const override;
+
+  static std::string GetRootEndpointPath();
+  static std::string GetEndpointPath(const std::string& endpoint_path);
+
+ private:
+  explicit ClientChannelFactory(const std::string& endpoint_path);
+  explicit ClientChannelFactory(LocalHandle socket);
+
+  mutable LocalHandle socket_;
+  std::string endpoint_path_;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
new file mode 100644
index 0000000..bde16d3
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_PDX_UDS_IPC_HELPER_H_
+#define ANDROID_PDX_UDS_IPC_HELPER_H_
+
+#include <sys/socket.h>
+#include <utility>
+#include <vector>
+
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/status.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+// Test interfaces used for unit-testing payload sending/receiving over sockets.
+class SendInterface {
+ public:
+  virtual ssize_t Send(int socket_fd, const void* data, size_t size,
+                       int flags) = 0;
+  virtual ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) = 0;
+
+ protected:
+  virtual ~SendInterface() = default;
+};
+
+class RecvInterface {
+ public:
+  virtual ssize_t Receive(int socket_fd, void* data, size_t size,
+                          int flags) = 0;
+  virtual ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) = 0;
+
+ protected:
+  virtual ~RecvInterface() = default;
+};
+
+// Helper methods that allow to send/receive data through abstract interfaces.
+// Useful for mocking out the underlying socket I/O.
+Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+                     const void* data, size_t size);
+Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+                        const msghdr* msg);
+Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd,
+                     void* data, size_t size);
+Status<void> RecvMsgAll(RecvInterface* receiver,
+                        const BorrowedHandle& socket_fd, msghdr* msg);
+
+#define RETRY_EINTR(fnc_call)                 \
+  ([&]() -> decltype(fnc_call) {              \
+    decltype(fnc_call) result;                \
+    do {                                      \
+      result = (fnc_call);                    \
+    } while (result == -1 && errno == EINTR); \
+    return result;                            \
+  })()
+
+class SendPayload : public MessageWriter, public OutputResourceMapper {
+ public:
+  SendPayload(SendInterface* sender = nullptr) : sender_{sender} {}
+  Status<void> Send(const BorrowedHandle& socket_fd);
+  Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred);
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override;
+  OutputResourceMapper* GetOutputResourceMapper() override;
+
+  // OutputResourceMapper
+  Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+  Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+  Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const LocalChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      const RemoteChannelHandle& handle) override;
+
+ private:
+  SendInterface* sender_;
+  ByteBuffer buffer_;
+  std::vector<int> file_handles_;
+};
+
+class ReceivePayload : public MessageReader, public InputResourceMapper {
+ public:
+  ReceivePayload(RecvInterface* receiver = nullptr) : receiver_{receiver} {}
+  Status<void> Receive(const BorrowedHandle& socket_fd);
+  Status<void> Receive(const BorrowedHandle& socket_fd, ucred* cred);
+
+  // MessageReader
+  BufferSection GetNextReadBufferSection() override;
+  void ConsumeReadBufferSectionData(const void* new_start) override;
+  InputResourceMapper* GetInputResourceMapper() override;
+
+  // InputResourceMapper
+  bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+  bool GetChannelHandle(ChannelReference ref,
+                        LocalChannelHandle* handle) override;
+
+ private:
+  RecvInterface* receiver_;
+  ByteBuffer buffer_;
+  std::vector<LocalHandle> file_handles_;
+  size_t read_pos_{0};
+};
+
+template <typename FileHandleType>
+class ChannelInfo {
+ public:
+  FileHandleType data_fd;
+  FileHandleType event_fd;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd);
+};
+
+template <typename FileHandleType>
+class ChannelConnectionInfo {
+ public:
+  FileHandleType channel_fd;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(ChannelConnectionInfo, channel_fd);
+};
+
+template <typename FileHandleType>
+class RequestHeader {
+ public:
+  int32_t op{0};
+  ucred cred;
+  uint32_t send_len{0};
+  uint32_t max_recv_len{0};
+  std::vector<FileHandleType> file_descriptors;
+  std::vector<ChannelInfo<FileHandleType>> channels;
+  std::array<uint8_t, 32> impulse_payload;
+  bool is_impulse{false};
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(RequestHeader, op, send_len, max_recv_len,
+                           file_descriptors, channels, impulse_payload,
+                           is_impulse);
+};
+
+template <typename FileHandleType>
+class ResponseHeader {
+ public:
+  int32_t ret_code{0};
+  uint32_t recv_len{0};
+  std::vector<FileHandleType> file_descriptors;
+  std::vector<ChannelInfo<FileHandleType>> channels;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(ResponseHeader, ret_code, recv_len, file_descriptors,
+                           channels);
+};
+
+template <typename T>
+inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) {
+  SendPayload payload;
+  rpc::Serialize(data, &payload);
+  return payload.Send(socket_fd);
+}
+
+template <typename FileHandleType>
+inline Status<void> SendData(const BorrowedHandle& socket_fd,
+                             const RequestHeader<FileHandleType>& request) {
+  SendPayload payload;
+  rpc::Serialize(request, &payload);
+  return payload.Send(socket_fd, &request.cred);
+}
+
+Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
+                      size_t size);
+Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data,
+                            size_t count);
+
+template <typename T>
+inline Status<void> ReceiveData(const BorrowedHandle& socket_fd, T* data) {
+  ReceivePayload payload;
+  Status<void> status = payload.Receive(socket_fd);
+  if (status && rpc::Deserialize(data, &payload) != rpc::ErrorCode::NO_ERROR)
+    status.SetError(EIO);
+  return status;
+}
+
+template <typename FileHandleType>
+inline Status<void> ReceiveData(const BorrowedHandle& socket_fd,
+                                RequestHeader<FileHandleType>* request) {
+  ReceivePayload payload;
+  Status<void> status = payload.Receive(socket_fd, &request->cred);
+  if (status && rpc::Deserialize(request, &payload) != rpc::ErrorCode::NO_ERROR)
+    status.SetError(EIO);
+  return status;
+}
+
+Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data,
+                         size_t size);
+Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd,
+                               const iovec* data, size_t count);
+
+size_t CountVectorSize(const iovec* data, size_t count);
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+                 int opcode, uint32_t send_len, uint32_t max_recv_len,
+                 bool is_impulse);
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+                             int64_t timeout_ms);
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_IPC_HELPER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
new file mode 100644
index 0000000..23af4f4
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
@@ -0,0 +1,55 @@
+#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
new file mode 100644
index 0000000..368891c
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -0,0 +1,167 @@
+#ifndef ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+
+#include <sys/stat.h>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#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 {
+namespace uds {
+
+class Endpoint : public pdx::Endpoint {
+ public:
+  enum {
+    kIpcTag = 0x00736674,  // 'uds'
+  };
+
+  // Blocking modes for service endpoint. Controls whether the epoll set is in
+  // blocking mode or not for message receive.
+  enum {
+    kBlocking = true,
+    kNonBlocking = false,
+    kDefaultBlocking = kNonBlocking,
+  };
+
+  enum : mode_t {
+    kDefaultMode = 0,
+  };
+
+  ~Endpoint() override = default;
+
+  uint32_t GetIpcTag() const override { return kIpcTag; }
+  Status<void> SetService(Service* service) override;
+  Status<void> SetChannel(int channel_id, Channel* channel) override;
+  Status<void> CloseChannel(int channel_id) override;
+  Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+                                   int set_mask) override;
+  Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+                                          Channel* channel,
+                                          int* channel_id) override;
+  Status<int> CheckChannel(const Message* message, ChannelReference ref,
+                           Channel** channel) override;
+  Status<void> MessageReceive(Message* message) override;
+  Status<void> MessageReply(Message* message, int return_code) override;
+  Status<void> MessageReplyFd(Message* message, unsigned int push_fd) override;
+  Status<void> MessageReplyChannelHandle(
+      Message* message, const LocalChannelHandle& handle) override;
+  Status<void> MessageReplyChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) override;
+  Status<void> MessageReplyChannelHandle(
+      Message* message, const RemoteChannelHandle& handle) override;
+  Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+                                 size_t vector_length) override;
+  Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+                                  size_t vector_length) override;
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const LocalHandle& handle) override;
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const BorrowedHandle& handle) override;
+  Status<FileReference> PushFileHandle(Message* message,
+                                       const RemoteHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      Message* message, const LocalChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) override;
+  Status<ChannelReference> PushChannelHandle(
+      Message* message, const RemoteChannelHandle& handle) override;
+  LocalHandle GetFileHandle(Message* message, FileReference ref) const override;
+  LocalChannelHandle GetChannelHandle(Message* message,
+                                      ChannelReference ref) const override;
+
+  void* AllocateMessageState() override;
+  void FreeMessageState(void* state) override;
+
+  Status<void> Cancel() override;
+
+  // Open an endpoint at the given path.
+  // Second parameter is unused for UDS, but we have it here for compatibility
+  // in signature with servicefs::Endpoint::Create().
+  // This method uses |endpoint_path| as a relative path to endpoint socket
+  // created by init process.
+  static std::unique_ptr<Endpoint> Create(const std::string& endpoint_path,
+                                          mode_t /*unused_mode*/ = kDefaultMode,
+                                          bool blocking = kDefaultBlocking);
+
+  // Helper method to create an endpoint at the given UDS socket path. This
+  // method physically creates and binds a socket at that path.
+  static std::unique_ptr<Endpoint> CreateAndBindSocket(
+      const std::string& endpoint_path, bool blocking = kDefaultBlocking);
+
+  // Helper method to create an endpoint from an existing socket FD.
+  // Mostly helpful for tests.
+  static std::unique_ptr<Endpoint> CreateFromSocketFd(LocalHandle socket_fd);
+
+  // Test helper method to register a new channel identified by |channel_fd|
+  // socket file descriptor.
+  Status<void> RegisterNewChannelForTests(LocalHandle channel_fd);
+
+  int epoll_fd() const { return epoll_fd_.Get(); }
+
+ private:
+  struct ChannelData {
+    LocalHandle data_fd;
+    ChannelEventSet event_set;
+    Channel* channel_state{nullptr};
+  };
+
+  // This class must be instantiated using Create() static methods above.
+  Endpoint(const std::string& endpoint_path, bool blocking,
+           bool use_init_socket_fd = true);
+  Endpoint(LocalHandle socket_fd);
+
+  void Init(LocalHandle socket_fd);
+
+  Endpoint(const Endpoint&) = delete;
+  void operator=(const Endpoint&) = delete;
+
+  uint32_t GetNextAvailableMessageId() {
+    return next_message_id_.fetch_add(1, std::memory_order_relaxed);
+  }
+
+  void BuildCloseMessage(int32_t channel_id, Message* message);
+
+  Status<void> AcceptConnection(Message* message);
+  Status<void> ReceiveMessageForChannel(const BorrowedHandle& channel_fd,
+                                        Message* message);
+  Status<void> OnNewChannel(LocalHandle channel_fd);
+  Status<std::pair<int32_t, ChannelData*>> OnNewChannelLocked(
+      LocalHandle channel_fd, Channel* channel_state);
+  Status<void> CloseChannelLocked(int32_t channel_id);
+  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);
+  int32_t GetChannelId(const BorrowedHandle& channel_fd);
+  Status<void> CreateChannelSocketPair(LocalHandle* local_socket,
+                                       LocalHandle* remote_socket);
+
+  std::string endpoint_path_;
+  bool is_blocking_;
+  LocalHandle socket_fd_;
+  LocalHandle cancel_event_fd_;
+  LocalHandle epoll_fd_;
+
+  mutable std::mutex channel_mutex_;
+  std::map<int32_t, ChannelData> channels_;
+  std::map<int, int32_t> channel_fd_to_id_;
+  int32_t last_channel_id_{0};
+
+  Service* service_{nullptr};
+  std::atomic<uint32_t> next_message_id_;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_uds/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp
new file mode 100644
index 0000000..3109753
--- /dev/null
+++ b/libs/vr/libpdx_uds/remote_method_tests.cpp
@@ -0,0 +1,951 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.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;
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::uds::Endpoint;
+using namespace android::pdx::rpc;
+
+namespace {
+
+std::string Rot13(const std::string& s) {
+  std::string text = s;
+  std::transform(std::begin(text), std::end(text), std::begin(text),
+                 [](char c) -> char {
+                   if (!std::isalpha(c)) {
+                     return c;
+                   } else {
+                     const char pivot = std::isupper(c) ? 'A' : 'a';
+                     return (c - pivot + 13) % 26 + pivot;
+                   }
+                 });
+  return text;
+}
+
+// Defines a serializable user type that may be transferred between client and
+// service.
+struct TestType {
+  int a;
+  float b;
+  std::string c;
+
+  TestType() {}
+  TestType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+  // Make gtest expressions simpler by defining equality operator. This is not
+  // needed for serialization.
+  bool operator==(const TestType& other) const {
+    return a == other.a && b == other.b && c == other.c;
+  }
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c);
+};
+
+struct DerivedTestType : public TestType {
+  DerivedTestType() : TestType() {}
+  DerivedTestType(int a, float b) : TestType(a, b, "constant") {}
+};
+
+// Defines a serializable user type with a LocalHandle member.
+struct TestFdType {
+  int a;
+  LocalHandle fd;
+
+  TestFdType() {}
+  TestFdType(int a, LocalHandle fd) : a(a), fd(std::move(fd)) {}
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(TestFdType, a, fd);
+};
+
+// Defines a serializable user template type with a FileHandle member.
+template <typename FileHandleType>
+struct TestTemplateType {
+  FileHandleType fd;
+
+  TestTemplateType() {}
+  TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+struct BasicStruct {
+  int a;
+  int b;
+  std::string c;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(BasicStruct, a, b, c);
+};
+
+using BasicStructTraits = SerializableTraits<BasicStruct>;
+
+struct NonSerializableType {
+  int a;
+  int b;
+  std::string c;
+};
+
+struct IncorrectlyDefinedSerializableType {
+  int a;
+  int b;
+
+ private:
+  using SerializableMembers = std::tuple<int, int>;
+};
+
+// Defines the contract between the client and service, including ServiceFS
+// endpoint path, method opcodes, and remote method signatures.
+struct TestInterface final {
+  // Service path.
+  static constexpr char kClientPath[] = "socket_test";
+
+  // Op codes.
+  enum {
+    kOpAdd = 0,
+    kOpFoo,
+    kOpConcatenate,
+    kOpWriteBuffer,
+    kOpStringLength,
+    kOpSendTestType,
+    kOpSendBasicStruct,
+    kOpSendVector,
+    kOpRot13,
+    kOpNoArgs,
+    kOpSendFile,
+    kOpGetFile,
+    kOpGetTestFdType,
+    kOpOpenFiles,
+    kOpReadFile,
+    kOpPushChannel,
+    kOpPositive,
+  };
+
+  // Methods.
+  PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+  PDX_REMOTE_METHOD(Foo, kOpFoo, int(int, const std::string&));
+  PDX_REMOTE_METHOD(Concatenate, kOpConcatenate,
+                    std::string(const std::string&, const std::string&));
+  PDX_REMOTE_METHOD(SumVector, kOpWriteBuffer, int(const std::vector<int>&));
+  PDX_REMOTE_METHOD(StringLength, kOpStringLength, int(const std::string&));
+  PDX_REMOTE_METHOD(SendTestType, kOpSendTestType, TestType(const TestType&));
+  PDX_REMOTE_METHOD(SendBasicStruct, kOpSendBasicStruct,
+                    BasicStruct(const BasicStruct&));
+  PDX_REMOTE_METHOD(SendVector, kOpSendVector,
+                    std::string(const std::vector<TestType>&));
+  PDX_REMOTE_METHOD(Rot13, kOpRot13, std::string(const std::string&));
+  PDX_REMOTE_METHOD(NoArgs, kOpNoArgs, int(Void));
+  PDX_REMOTE_METHOD(SendFile, kOpSendFile, int(const LocalHandle& fd));
+  PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+  PDX_REMOTE_METHOD(GetTestFdType, kOpGetTestFdType,
+                    TestFdType(int, const std::string&, int));
+  PDX_REMOTE_METHOD(OpenFiles, kOpOpenFiles,
+                    std::vector<LocalHandle>(
+                        const std::vector<std::pair<std::string, int>>&));
+  PDX_REMOTE_METHOD(ReadFile, kOpReadFile,
+                    std::pair<int, BufferWrapper<std::uint8_t*>>(
+                        const std::string&, int, std::size_t));
+  PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(Positive, kOpPositive, void(int));
+
+  PDX_REMOTE_API(API, Add, Foo, Concatenate, SumVector, StringLength,
+                 SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile,
+                 GetTestFdType, OpenFiles, PushChannel, Positive);
+};
+
+constexpr char TestInterface::kClientPath[];
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+  int Add(int a, int b) {
+    return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Add>(a, b));
+  }
+
+  int Foo(int a, const std::string& b) {
+    return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Foo>(a, b));
+  }
+
+  std::string Concatenate(const std::string& a, const std::string& b) {
+    std::string return_value;
+
+    Status<std::string> status =
+        InvokeRemoteMethod<TestInterface::Concatenate>(a, b);
+    if (!status)
+      return std::string("[Error]");
+    else
+      return status.take();
+  }
+
+  int SumVector(const int* buffer, std::size_t size) {
+    return ReturnStatusOrError(
+        InvokeRemoteMethod<TestInterface::SumVector>(WrapArray(buffer, size)));
+  }
+
+  int SumVector(const std::vector<int>& buffer) {
+    return ReturnStatusOrError(
+        InvokeRemoteMethod<TestInterface::SumVector>(buffer));
+  }
+
+  int StringLength(const char* string, std::size_t size) {
+    return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::StringLength>(
+        WrapString(string, size)));
+  }
+
+  int StringLength(const std::string& string) {
+    return ReturnStatusOrError(
+        InvokeRemoteMethod<TestInterface::StringLength>(string));
+  }
+
+  TestType SendTestType(const TestType& tt) {
+    Status<TestType> status =
+        InvokeRemoteMethod<TestInterface::SendTestType>(tt);
+    if (!status)
+      return TestType(0, 0.0, "[Error]");
+    else
+      return status.take();
+  }
+
+  BasicStruct SendBasicStruct(const BasicStruct& bs) {
+    Status<BasicStruct> status =
+        InvokeRemoteMethod<TestInterface::SendBasicStruct>(bs);
+    if (!status)
+      return BasicStruct{0, 0, "[Error]"};
+    else
+      return status.take();
+  }
+
+  std::string SendVector(const std::vector<TestType>& v) {
+    Status<std::string> status =
+        InvokeRemoteMethod<TestInterface::SendVector>(v);
+    if (!status)
+      return "[Error]";
+    else
+      return status.take();
+  }
+
+  std::string Rot13(const std::string& string) {
+    Status<std::string> status =
+        InvokeRemoteMethod<TestInterface::Rot13>(string);
+    return status ? status.get() : string;
+  }
+
+  int NoArgs() {
+    return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::NoArgs>());
+  }
+
+  int SendFile(const LocalHandle& fd) {
+    return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::SendFile>(fd));
+  }
+
+  LocalHandle GetFile(const std::string& path, int mode) {
+    Status<LocalHandle> status =
+        InvokeRemoteMethod<TestInterface::GetFile>(path, mode);
+    if (!status)
+      return LocalHandle(-status.error());
+    else
+      return status.take();
+  }
+
+  int GetFile(const std::string& path, int mode, LocalHandle* fd_out) {
+    Status<void> status =
+        InvokeRemoteMethodInPlace<TestInterface::GetFile>(fd_out, path, mode);
+    return status ? 0 : -status.error();
+  }
+
+  TestFdType GetTestFdType(int a, const std::string& path, int mode) {
+    Status<TestFdType> status =
+        InvokeRemoteMethod<TestInterface::GetTestFdType>(a, path, mode);
+    if (!status)
+      return {};
+    else
+      return status.take();
+  }
+
+  std::vector<LocalHandle> OpenFiles(
+      const std::vector<std::pair<std::string, int>>& file_specs) {
+    Status<std::vector<LocalHandle>> status =
+        InvokeRemoteMethod<TestInterface::OpenFiles>(file_specs);
+    if (!status)
+      return {};
+    else
+      return status.take();
+  }
+
+  int ReadFile(void* buffer, std::size_t size, const std::string& path,
+               int mode) {
+    auto buffer_wrapper = WrapBuffer(buffer, size);
+    auto return_value = std::make_pair(-1, buffer_wrapper);
+
+    Status<void> status = InvokeRemoteMethodInPlace<TestInterface::ReadFile>(
+        &return_value, path, mode, size);
+    return status ? return_value.first : -status.error();
+  }
+
+  int PushChannel(LocalChannelHandle* fd_out) {
+    auto status = InvokeRemoteMethodInPlace<TestInterface::PushChannel>(fd_out);
+    return status ? 0 : -status.error();
+  }
+
+  bool Positive(int test_value) {
+    auto status = InvokeRemoteMethod<TestInterface::Positive>(test_value);
+    return status.ok();
+  }
+
+  int GetFd() const { return event_fd(); }
+
+ private:
+  friend BASE;
+
+  TestClient(LocalChannelHandle channel_handle)
+      : BASE{android::pdx::uds::ClientChannel::Create(
+            std::move(channel_handle))} {}
+  TestClient()
+      : BASE{android::pdx::uds::ClientChannelFactory::Create(
+            TestInterface::kClientPath)} {}
+
+  TestClient(const TestClient&) = delete;
+  void operator=(const TestClient&) = delete;
+};
+
+// Test service that encodes/decodes messages from clients.
+class TestService : public ServiceBase<TestService> {
+ public:
+  Status<void> HandleMessage(Message& message) override {
+    switch (message.GetOp()) {
+      case TestInterface::Add::Opcode:
+        DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd,
+                                                 message);
+        return {};
+
+      case TestInterface::Foo::Opcode:
+        DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo,
+                                                 message);
+        return {};
+
+      case TestInterface::Concatenate::Opcode:
+        DispatchRemoteMethod<TestInterface::Concatenate>(
+            *this, &TestService::OnConcatenate, message);
+        return {};
+
+      case TestInterface::SumVector::Opcode:
+        DispatchRemoteMethod<TestInterface::SumVector>(
+            *this, &TestService::OnSumVector, message);
+        return {};
+
+      case TestInterface::StringLength::Opcode:
+        DispatchRemoteMethod<TestInterface::StringLength>(
+            *this, &TestService::OnStringLength, message);
+        return {};
+
+      case TestInterface::SendTestType::Opcode:
+        DispatchRemoteMethod<TestInterface::SendTestType>(
+            *this, &TestService::OnSendTestType, message);
+        return {};
+
+      case TestInterface::SendVector::Opcode:
+        DispatchRemoteMethod<TestInterface::SendVector>(
+            *this, &TestService::OnSendVector, message);
+        return {};
+
+      case TestInterface::Rot13::Opcode:
+        DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13,
+                                                   message);
+        return {};
+
+      case TestInterface::NoArgs::Opcode:
+        DispatchRemoteMethod<TestInterface::NoArgs>(
+            *this, &TestService::OnNoArgs, message);
+        return {};
+
+      case TestInterface::SendFile::Opcode:
+        DispatchRemoteMethod<TestInterface::SendFile>(
+            *this, &TestService::OnSendFile, message);
+        return {};
+
+      case TestInterface::GetFile::Opcode:
+        DispatchRemoteMethod<TestInterface::GetFile>(
+            *this, &TestService::OnGetFile, message);
+        return {};
+
+      case TestInterface::GetTestFdType::Opcode:
+        DispatchRemoteMethod<TestInterface::GetTestFdType>(
+            *this, &TestService::OnGetTestFdType, message);
+        return {};
+
+      case TestInterface::OpenFiles::Opcode:
+        DispatchRemoteMethod<TestInterface::OpenFiles>(
+            *this, &TestService::OnOpenFiles, message);
+        return {};
+
+      case TestInterface::ReadFile::Opcode:
+        DispatchRemoteMethod<TestInterface::ReadFile>(
+            *this, &TestService::OnReadFile, message);
+        return {};
+
+      case TestInterface::PushChannel::Opcode:
+        DispatchRemoteMethod<TestInterface::PushChannel>(
+            *this, &TestService::OnPushChannel, message);
+        return {};
+
+      case TestInterface::Positive::Opcode:
+        DispatchRemoteMethod<TestInterface::Positive>(
+            *this, &TestService::OnPositive, message);
+        return {};
+
+      default:
+        return Service::DefaultHandleMessage(message);
+    }
+  }
+
+ private:
+  friend BASE;
+
+  TestService()
+      : BASE("TestService",
+             Endpoint::CreateAndBindSocket(TestInterface::kClientPath)) {}
+
+  int OnAdd(Message&, int a, int b) { return a + b; }
+
+  int OnFoo(Message&, int a, const std::string& b) { return a + b.length(); }
+
+  std::string OnConcatenate(Message&, const std::string& a,
+                            const std::string& b) {
+    return a + b;
+  }
+
+  int OnSumVector(Message&, const std::vector<int>& vector) {
+    return std::accumulate(vector.begin(), vector.end(), 0);
+  }
+
+  int OnStringLength(Message&, const std::string& string) {
+    return string.length();
+  }
+
+  TestType OnSendTestType(Message&, const TestType& tt) {
+    return TestType(tt.a + 20, tt.b - 2.0, tt.c + "foo");
+  }
+
+  std::string OnSendVector(Message&, const std::vector<TestType>& v) {
+    std::string return_value = "";
+
+    for (const auto& tt : v)
+      return_value += tt.c;
+
+    return return_value;
+  }
+
+  Status<std::string> OnRot13(Message&, const std::string& s) {
+    return {Rot13(s)};
+  }
+
+  int OnNoArgs(Message&) { return 1; }
+
+  int OnSendFile(Message&, const LocalHandle& fd) { return fd.Get(); }
+
+  LocalHandle OnGetFile(Message& message, const std::string& path, int mode) {
+    LocalHandle fd(path.c_str(), mode);
+    if (!fd)
+      message.ReplyError(errno);
+    return fd;
+  }
+
+  TestFdType OnGetTestFdType(Message& message, int a, const std::string& path,
+                             int mode) {
+    TestFdType return_value(a, LocalHandle(path, mode));
+    if (!return_value.fd)
+      message.ReplyError(errno);
+    return return_value;
+  }
+
+  std::vector<LocalHandle> OnOpenFiles(
+      Message&, const std::vector<std::pair<std::string, int>>& file_specs) {
+    std::vector<LocalHandle> return_value;
+    for (auto& spec : file_specs) {
+      LocalHandle fd(spec.first, spec.second);
+      if (fd)
+        return_value.emplace_back(std::move(fd));
+      else
+        return_value.emplace_back(-errno);
+    }
+    return return_value;
+  }
+
+  std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> OnReadFile(
+      Message& message, const std::string& path, int mode, std::size_t length) {
+    std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> return_value;
+    LocalHandle fd(path, mode);
+    if (!fd) {
+      message.ReplyError(errno);
+      return return_value;
+    }
+
+    return_value.second.reserve(length);
+    const int ret = read(fd.Get(), return_value.second.data(), length);
+    if (ret < 0) {
+      message.ReplyError(errno);
+      return return_value;
+    }
+
+    return_value.second.resize(ret);
+    return_value.first = ret;
+    return return_value;
+  }
+
+  RemoteChannelHandle OnPushChannel(Message& message) {
+    auto status = message.PushChannel(0, nullptr, nullptr);
+    if (!status) {
+      message.ReplyError(status.error());
+      return {};
+    }
+    return status.take();
+  }
+
+  Status<void> OnPositive(Message& /*message*/, int test_value) {
+    if (test_value >= 0)
+      return {};
+    else
+      return ErrorStatus(EINVAL);
+  }
+
+  TestService(const TestService&) = delete;
+  void operator=(const TestService&) = delete;
+};
+
+}  // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. As these objects are cleaned up in the same
+// thread, either the service or client must be destroyed before stopping the
+// dispatcher. The reason for this is that clients send blocking "close"
+// messages to their respective services on destruction. If this happens after
+// stopping the dispatcher the client destructor will get blocked waiting for a
+// reply that will never come. In normal use of the service framework this is
+// never an issue because clients and the dispatcher for the same service are
+// never destructed in the same thread (they live in different processes).
+class RemoteMethodTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<ServiceDispatcher> dispatcher_;
+  std::thread dispatch_thread_;
+
+  void SetUp() override {
+    // Create a dispatcher to handle messages to services.
+    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    ASSERT_NE(nullptr, dispatcher_);
+
+    // Start the message dispatch loop in a separate thread.
+    dispatch_thread_ = std::thread(
+        std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+  }
+
+  void TearDown() override {
+    if (dispatcher_) {
+      // Cancel the dispatcher and wait for the thread to terminate.
+      // Explicitly
+      // join the thread so that destruction doesn't deallocate the
+      // dispatcher
+      // before the thread finishes.
+      dispatcher_->SetCanceled(true);
+      dispatch_thread_.join();
+    }
+  }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(RemoteMethodTest, BasicClientService) {
+  // Create a test service and add it to the dispatcher.
+
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  const int sum = client->Add(10, 25);
+  EXPECT_GE(35, sum);
+
+  const auto cat = client->Concatenate("This is a string", ", that it is.");
+  EXPECT_EQ("This is a string, that it is.", cat);
+
+  std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
+  const auto rot13_alphabet = client->Rot13(alphabet);
+  EXPECT_EQ(Rot13(alphabet), rot13_alphabet);
+
+  const auto length = client->Foo(10, "123");
+  EXPECT_EQ(13, length);
+
+  const std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  const int vector_sum = client->SumVector(vector.data(), vector.size());
+  const int vector_sum2 = client->SumVector(vector);
+  EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum);
+  EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum2);
+
+  const auto string_length1 = client->StringLength("This is a string");
+  EXPECT_EQ(16, string_length1);
+
+  const auto string_length2 = client->StringLength("1234567890");
+  EXPECT_EQ(10, string_length2);
+
+  std::string string = "1234567890";
+  const auto string_length3 =
+      client->StringLength(string.c_str(), string.length());
+  EXPECT_EQ(10, string_length3);
+
+  TestType tt{10, 0.0, "string"};
+  const auto tt_result = client->SendTestType(tt);
+  EXPECT_EQ(TestType(30, -2.0, "stringfoo"), tt_result);
+
+  std::vector<TestType> ttv = {TestType(0, 0.0, "abc"),
+                               TestType(0, 0.0, "123")};
+  const std::string string_result = client->SendVector(ttv);
+  EXPECT_EQ("abc123", string_result);
+
+  const int int_result = client->NoArgs();
+  EXPECT_EQ(1, int_result);
+}
+
+TEST_F(RemoteMethodTest, LocalHandle) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  LocalHandle fd("/dev/zero", O_RDONLY);
+  ASSERT_TRUE(fd.IsValid());
+
+  int fd_result = client->SendFile(fd);
+  EXPECT_LE(0, fd_result);
+  EXPECT_NE(fd.Get(), fd_result);
+  fd = LocalHandle(-3);
+  fd_result = client->SendFile(fd);
+  EXPECT_EQ(fd.Get(), fd_result);
+
+  fd = client->GetFile("/dev/zero", O_RDONLY);
+  ASSERT_TRUE(fd.IsValid()) << "Error code: " << fd.Get();
+
+  std::array<uint8_t, 10> buffer;
+  buffer.fill(1);
+  EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+  EXPECT_EQ(buffer, decltype(buffer){{0}});
+  fd.Close();
+
+  const int error = client->GetFile("/dev/zero", O_RDONLY, &fd);
+  EXPECT_EQ(0, error);
+  EXPECT_TRUE(fd.IsValid());
+
+  buffer.fill(1);
+  EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+  EXPECT_EQ(buffer, decltype(buffer){{0}});
+
+  /*
+    Seg fault.
+    fd = client->GetFile("/dev/foobar", O_RDONLY);
+    EXPECT_FALSE(fd.IsValid());
+   */
+}
+
+TEST_F(RemoteMethodTest, PushChannel) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  // Get a new channel as an fd.
+  LocalChannelHandle channel;
+  const int ret = client->PushChannel(&channel);
+  EXPECT_EQ(0, ret);
+  EXPECT_TRUE(channel.valid());
+
+  // Create a new client from the channel.
+  auto client2 = TestClient::Create(std::move(channel));
+  ASSERT_NE(nullptr, client2);
+
+  // Test that the new channel works.
+  const int sum = client2->Add(10, 25);
+  EXPECT_GE(35, sum);
+}
+
+TEST_F(RemoteMethodTest, Positive) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  ASSERT_TRUE(client->Positive(0));
+  ASSERT_TRUE(client->Positive(1));
+  ASSERT_FALSE(client->Positive(-1));
+}
+
+TEST_F(RemoteMethodTest, AggregateLocalHandle) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  TestFdType result = client->GetTestFdType(10, "/dev/zero", O_RDONLY);
+  EXPECT_TRUE(result.fd.IsValid());
+  EXPECT_EQ(10, result.a);
+
+  std::vector<LocalHandle> files =
+      client->OpenFiles({{{"/dev/zero", O_RDONLY},
+                          {"/dev/null", O_WRONLY},
+                          {"/dev/zero", O_RDONLY}}});
+  ASSERT_EQ(3u, files.size());
+  EXPECT_TRUE(files[0].IsValid());
+  EXPECT_TRUE(files[1].IsValid());
+  EXPECT_TRUE(files[2].IsValid());
+}
+
+TEST_F(RemoteMethodTest, BufferWrapper) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create();
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create();
+  ASSERT_NE(nullptr, client);
+
+  const int buffer_size = 20;
+  std::vector<std::uint8_t> buffer(buffer_size, 'x');
+  std::vector<std::uint8_t> expected(buffer_size, 0);
+  int ret =
+      client->ReadFile(buffer.data(), buffer.size(), "/dev/zero", O_RDONLY);
+  EXPECT_EQ(buffer_size, ret);
+  EXPECT_EQ(expected, buffer);
+}
+
+//
+// RemoteMethodFramework: Tests the type-based framework that remote method
+// support is built upon.
+//
+
+// Test logical And template.
+TEST(RemoteMethodFramework, And) {
+  EXPECT_TRUE((And<std::true_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::true_type, std::false_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::false_type>::value));
+
+  EXPECT_TRUE((And<std::true_type, std::true_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::true_type, std::true_type, std::false_type>::value));
+  EXPECT_FALSE((And<std::true_type, std::false_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::true_type, std::false_type, std::false_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::true_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::true_type, std::false_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::false_type, std::true_type>::value));
+  EXPECT_FALSE((And<std::false_type, std::false_type, std::false_type>::value));
+}
+
+// Test convertible type constraints.
+TEST(RemoteMethodFramework, IsConvertible) {
+  // std::pair.
+  EXPECT_TRUE(
+      (IsConvertible<std::pair<int, float>, std::pair<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::pair<int, float>, std::pair<float, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::pair<int, float>, std::pair<float, int>>::value));
+
+  // Nested std::pair.
+  EXPECT_TRUE((IsConvertible<std::pair<std::pair<int, float>, float>,
+                             std::pair<std::pair<int, float>, float>>::value));
+  EXPECT_FALSE((IsConvertible<std::pair<std::pair<int, float>, float>,
+                              std::pair<std::pair<float, int>, float>>::value));
+
+  // std::tuple and std::pair.
+  EXPECT_TRUE(
+      (IsConvertible<std::pair<int, float>, std::tuple<int, float>>::value));
+  EXPECT_TRUE(
+      (IsConvertible<std::tuple<int, float>, std::pair<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::pair<float, float>, std::tuple<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::tuple<float, float>, std::pair<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::pair<int, int>, std::tuple<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::tuple<int, int>, std::pair<int, float>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::pair<int, int>, std::tuple<int, int, int>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<std::tuple<int, int, int>, std::pair<int, int>>::value));
+
+  // std::vector.
+  EXPECT_TRUE((IsConvertible<std::vector<int>, std::vector<int>>::value));
+  EXPECT_FALSE((IsConvertible<std::vector<int>, std::vector<float>>::value));
+
+  // Nested std::vector.
+  EXPECT_TRUE((IsConvertible<std::vector<std::pair<int, int>>,
+                             std::vector<std::pair<int, int>>>::value));
+  EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+                              std::vector<std::pair<int, float>>>::value));
+  EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+                              std::vector<std::pair<float, int>>>::value));
+  EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+                              std::vector<std::pair<float, float>>>::value));
+
+  // std::vector with nested convertible types.
+  EXPECT_TRUE((IsConvertible<std::vector<StringWrapper<char>>,
+                             std::vector<std::string>>::value));
+
+  // std::map and std::unordered_map.
+  EXPECT_TRUE((IsConvertible<std::map<int, float>,
+                             std::unordered_map<int, float>>::value));
+  EXPECT_FALSE((IsConvertible<std::map<float, float>,
+                              std::unordered_map<int, float>>::value));
+  EXPECT_FALSE((IsConvertible<std::map<float, float>,
+                              std::unordered_map<float, int>>::value));
+  EXPECT_FALSE((IsConvertible<std::map<float, float>,
+                              std::unordered_map<int, int>>::value));
+  EXPECT_TRUE((IsConvertible<std::unordered_map<int, float>,
+                             std::map<int, float>>::value));
+  EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+                              std::map<int, float>>::value));
+  EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+                              std::map<float, int>>::value));
+  EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+                              std::map<int, int>>::value));
+
+  // std::map with nested convertible types.
+  EXPECT_TRUE((IsConvertible<std::map<int, std::string>,
+                             std::map<int, StringWrapper<char>>>::value));
+  EXPECT_TRUE(
+      (IsConvertible<std::map<std::tuple<int, int>, std::string>,
+                     std::map<std::pair<int, int>, std::string>>::value));
+
+  // std::unordered_map with nested convertible types.
+  EXPECT_TRUE(
+      (IsConvertible<std::unordered_map<int, std::string>,
+                     std::unordered_map<int, StringWrapper<char>>>::value));
+  EXPECT_TRUE((IsConvertible<
+               std::unordered_map<std::tuple<int, int>, std::string>,
+               std::unordered_map<std::pair<int, int>, std::string>>::value));
+
+  // std::string.
+  EXPECT_TRUE((IsConvertible<std::string, std::string>::value));
+  EXPECT_FALSE((IsConvertible<std::string, int>::value));
+  EXPECT_FALSE((IsConvertible<int, std::string>::value));
+
+  // Nested std::string.
+  EXPECT_TRUE((IsConvertible<std::pair<std::string, std::string>,
+                             std::pair<std::string, std::string>>::value));
+  EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+                              std::pair<std::string, int>>::value));
+  EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+                              std::pair<int, std::string>>::value));
+  EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+                              std::pair<int, int>>::value));
+
+  // StringWrapper.
+  EXPECT_TRUE((IsConvertible<StringWrapper<char>, StringWrapper<char>>::value));
+  EXPECT_TRUE((IsConvertible<StringWrapper<char>, std::string>::value));
+  EXPECT_TRUE((IsConvertible<std::string, StringWrapper<char>>::value));
+  EXPECT_FALSE((IsConvertible<StringWrapper<char>, int>::value));
+  EXPECT_FALSE(
+      (IsConvertible<StringWrapper<char>, BufferWrapper<char*>>::value));
+
+  // BufferWrapper.
+  EXPECT_TRUE(
+      (IsConvertible<BufferWrapper<char*>, BufferWrapper<char*>>::value));
+  EXPECT_TRUE(
+      (IsConvertible<BufferWrapper<char*>, BufferWrapper<const char*>>::value));
+  EXPECT_FALSE(
+      (IsConvertible<BufferWrapper<char*>, BufferWrapper<int*>>::value));
+  EXPECT_TRUE((IsConvertible<BufferWrapper<char*>,
+                             BufferWrapper<std::vector<char>>>::value));
+
+  // RemoteHandle and BorrowedHandle.
+  EXPECT_TRUE((IsConvertible<LocalHandle, RemoteHandle>::value));
+  EXPECT_TRUE((IsConvertible<LocalHandle, BorrowedHandle>::value));
+
+  // Test rewriting user defined types.
+  EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+                             TestTemplateType<RemoteHandle>>::value));
+  EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+                             TestTemplateType<BorrowedHandle>>::value));
+  EXPECT_FALSE((IsConvertible<TestTemplateType<RemoteHandle>,
+                              TestTemplateType<LocalHandle>>::value));
+  EXPECT_FALSE((IsConvertible<TestTemplateType<BorrowedHandle>,
+                              TestTemplateType<LocalHandle>>::value));
+
+  // TODO(eieio): More thorough testing of convertible types.
+}
+
+TEST(RemoteMethodFramework, SerializableMembers) {
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+
+  EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+                  TestTemplateType<LocalHandle>>>::value);
+  EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+                  TestTemplateType<RemoteHandle>>>::value);
+  EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+                  TestTemplateType<BorrowedHandle>>>::value);
+
+  EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+
+  EXPECT_TRUE(HasSerializableMembers<BasicStruct>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestType>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+  EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+  EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+  EXPECT_FALSE(HasSerializableMembers<NonSerializableType>::value);
+  EXPECT_FALSE(
+      HasSerializableMembers<IncorrectlyDefinedSerializableType>::value);
+}
+
+TEST(RemoteMethodFramework, RemoteAPITypes) {
+  EXPECT_EQ(0u, TestInterface::API::MethodIndex<TestInterface::Add>());
+}
diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx_uds/service_dispatcher.cpp
new file mode 100644
index 0000000..2c52578
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_dispatcher.cpp
@@ -0,0 +1,202 @@
+#include "uds/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"
+
+static const int kMaxEventsPerLoop = 128;
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() {
+  std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()};
+  if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) {
+    dispatcher.reset();
+  }
+
+  return std::move(dispatcher);
+}
+
+ServiceDispatcher::ServiceDispatcher() {
+  event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  if (!event_fd_) {
+    ALOGE("Failed to create event fd because: %s\n", strerror(errno));
+    return;
+  }
+
+  epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+  if (!epoll_fd_) {
+    ALOGE("Failed to create epoll fd because: %s\n", strerror(errno));
+    return;
+  }
+
+  // Use "this" as a unique pointer to distinguish the event fd from all
+  // the other entries that point to instances of Service.
+  epoll_event event;
+  event.events = EPOLLIN;
+  event.data.ptr = this;
+
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
+    ALOGE("Failed to add event fd to epoll fd because: %s\n", strerror(errno));
+
+    // Close the fds here and signal failure to the factory method.
+    event_fd_.Close();
+    epoll_fd_.Close();
+  }
+}
+
+ServiceDispatcher::~ServiceDispatcher() { SetCanceled(true); }
+
+int ServiceDispatcher::ThreadEnter() {
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  if (canceled_)
+    return -EBUSY;
+
+  thread_count_++;
+  return 0;
+}
+
+void ServiceDispatcher::ThreadExit() {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  thread_count_--;
+  condition_.notify_one();
+}
+
+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) {
+    ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno));
+    return -errno;
+  }
+
+  services_.push_back(service);
+  return 0;
+}
+
+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.
+  if (thread_count_ > 0)
+    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) {
+    ALOGE("Failed to remove service from dispatcher because: %s\n",
+          strerror(errno));
+    return -errno;
+  }
+
+  services_.remove(service);
+  return 0;
+}
+
+int ServiceDispatcher::ReceiveAndDispatch() { return ReceiveAndDispatch(-1); }
+
+int ServiceDispatcher::ReceiveAndDispatch(int timeout) {
+  int ret = ThreadEnter();
+  if (ret < 0)
+    return ret;
+
+  epoll_event events[kMaxEventsPerLoop];
+
+  int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, timeout);
+  if (count <= 0) {
+    ALOGE_IF(count < 0, "Failed to wait for epoll events because: %s\n",
+             strerror(errno));
+    ThreadExit();
+    return count < 0 ? -errno : -ETIMEDOUT;
+  }
+
+  for (int i = 0; i < count; i++) {
+    if (events[i].data.ptr == this) {
+      ThreadExit();
+      return -EBUSY;
+    } else {
+      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->ReceiveAndDispatch();
+    }
+  }
+
+  ThreadExit();
+  return 0;
+}
+
+int ServiceDispatcher::EnterDispatchLoop() {
+  int ret = ThreadEnter();
+  if (ret < 0)
+    return ret;
+
+  epoll_event events[kMaxEventsPerLoop];
+
+  while (!IsCanceled()) {
+    int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, -1);
+    if (count < 0 && errno != EINTR) {
+      ALOGE("Failed to wait for epoll events because: %s\n", strerror(errno));
+      ThreadExit();
+      return -errno;
+    }
+
+    for (int i = 0; i < count; i++) {
+      if (events[i].data.ptr == this) {
+        ThreadExit();
+        return -EBUSY;
+      } else {
+        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->ReceiveAndDispatch();
+      }
+    }
+  }
+
+  ThreadExit();
+  return 0;
+}
+
+void ServiceDispatcher::SetCanceled(bool cancel) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  canceled_ = cancel;
+
+  if (canceled_ && thread_count_ > 0) {
+    eventfd_write(event_fd_.Get(), 1);  // Signal threads to quit.
+
+    condition_.wait(lock, [this] { return !(canceled_ && thread_count_ > 0); });
+
+    eventfd_t value;
+    eventfd_read(event_fd_.Get(), &value);  // Unsignal.
+  }
+}
+
+bool ServiceDispatcher::IsCanceled() const { return canceled_; }
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
new file mode 100644
index 0000000..27a56f9
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -0,0 +1,772 @@
+#include "uds/service_endpoint.h"
+
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <algorithm>  // std::min
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <cutils/sockets.h>
+#include <pdx/service.h>
+#include <selinux/selinux.h>
+#include <uds/channel_manager.h>
+#include <uds/client_channel_factory.h>
+#include <uds/ipc_helper.h>
+
+namespace {
+
+constexpr int kMaxBackLogForSocketListen = 1;
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+using android::pdx::uds::ChannelInfo;
+using android::pdx::uds::ChannelManager;
+
+struct MessageState {
+  bool GetLocalFileHandle(int index, LocalHandle* handle) {
+    if (index < 0) {
+      handle->Reset(index);
+    } else if (static_cast<size_t>(index) < request.file_descriptors.size()) {
+      *handle = std::move(request.file_descriptors[index]);
+    } else {
+      return false;
+    }
+    return true;
+  }
+
+  bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+    if (index < 0) {
+      *handle = LocalChannelHandle{nullptr, index};
+    } 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));
+    } else {
+      return false;
+    }
+    return true;
+  }
+
+  Status<FileReference> PushFileHandle(BorrowedHandle handle) {
+    if (!handle)
+      return handle.Get();
+    response.file_descriptors.push_back(std::move(handle));
+    return response.file_descriptors.size() - 1;
+  }
+
+  Status<ChannelReference> PushChannelHandle(BorrowedChannelHandle handle) {
+    if (!handle)
+      return handle.value();
+
+    if (auto* channel_data =
+            ChannelManager::Get().GetChannelData(handle.value())) {
+      ChannelInfo<BorrowedHandle> channel_info;
+      channel_info.data_fd.Reset(handle.value());
+      channel_info.event_fd = channel_data->event_receiver.event_fd();
+      response.channels.push_back(std::move(channel_info));
+      return response.channels.size() - 1;
+    } else {
+      return ErrorStatus{EINVAL};
+    }
+  }
+
+  Status<ChannelReference> PushChannelHandle(BorrowedHandle data_fd,
+                                             BorrowedHandle event_fd) {
+    if (!data_fd || !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);
+    response.channels.push_back(std::move(channel_info));
+    return response.channels.size() - 1;
+  }
+
+  Status<size_t> WriteData(const iovec* vector, size_t vector_length) {
+    size_t size = 0;
+    for (size_t i = 0; i < vector_length; i++) {
+      const auto* data = reinterpret_cast<const uint8_t*>(vector[i].iov_base);
+      response_data.insert(response_data.end(), data, data + vector[i].iov_len);
+      size += vector[i].iov_len;
+    }
+    return size;
+  }
+
+  Status<size_t> ReadData(const iovec* vector, size_t vector_length) {
+    size_t size_remaining = request_data.size() - request_data_read_pos;
+    size_t size = 0;
+    for (size_t i = 0; i < vector_length && size_remaining > 0; i++) {
+      size_t size_to_copy = std::min(size_remaining, vector[i].iov_len);
+      memcpy(vector[i].iov_base, request_data.data() + request_data_read_pos,
+             size_to_copy);
+      size += size_to_copy;
+      request_data_read_pos += size_to_copy;
+      size_remaining -= size_to_copy;
+    }
+    return size;
+  }
+
+  android::pdx::uds::RequestHeader<LocalHandle> request;
+  android::pdx::uds::ResponseHeader<BorrowedHandle> response;
+  std::vector<LocalHandle> sockets_to_close;
+  std::vector<uint8_t> request_data;
+  size_t request_data_read_pos{0};
+  std::vector<uint8_t> response_data;
+};
+
+}  // anonymous namespace
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+Endpoint::Endpoint(const std::string& endpoint_path, bool blocking,
+                   bool use_init_socket_fd)
+    : endpoint_path_{ClientChannelFactory::GetEndpointPath(endpoint_path)},
+      is_blocking_{blocking} {
+  LocalHandle fd;
+  if (use_init_socket_fd) {
+    // Cut off the /dev/socket/ prefix from the full socket path and use the
+    // resulting "name" to retrieve the file descriptor for the socket created
+    // by the init process.
+    constexpr char prefix[] = "/dev/socket/";
+    CHECK(android::base::StartsWith(endpoint_path_, prefix))
+        << "Endpoint::Endpoint: Socket name '" << endpoint_path_
+        << "' must begin with '" << prefix << "'";
+    std::string socket_name = endpoint_path_.substr(sizeof(prefix) - 1);
+    fd.Reset(android_get_control_socket(socket_name.c_str()));
+    CHECK(fd.IsValid())
+        << "Endpoint::Endpoint: Unable to obtain the control socket fd for '"
+        << socket_name << "'";
+    fcntl(fd.Get(), F_SETFD, FD_CLOEXEC);
+  } else {
+    fd.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+    CHECK(fd.IsValid()) << "Endpoint::Endpoint: Failed to create socket: "
+                        << strerror(errno);
+
+    sockaddr_un local;
+    local.sun_family = AF_UNIX;
+    strncpy(local.sun_path, endpoint_path_.c_str(), sizeof(local.sun_path));
+    local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+    unlink(local.sun_path);
+    int ret =
+        bind(fd.Get(), reinterpret_cast<sockaddr*>(&local), sizeof(local));
+    CHECK_EQ(ret, 0) << "Endpoint::Endpoint: bind error: " << strerror(errno);
+  }
+  Init(std::move(fd));
+}
+
+Endpoint::Endpoint(LocalHandle socket_fd) { Init(std::move(socket_fd)); }
+
+void Endpoint::Init(LocalHandle socket_fd) {
+  if (socket_fd) {
+    CHECK_EQ(listen(socket_fd.Get(), kMaxBackLogForSocketListen), 0)
+        << "Endpoint::Endpoint: listen error: " << strerror(errno);
+  }
+  cancel_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  CHECK(cancel_event_fd_.IsValid())
+      << "Endpoint::Endpoint: Failed to create event fd: " << strerror(errno);
+
+  epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+  CHECK(epoll_fd_.IsValid())
+      << "Endpoint::Endpoint: Failed to create epoll fd: " << strerror(errno);
+
+  if (socket_fd) {
+    epoll_event socket_event;
+    socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+    socket_event.data.fd = socket_fd.Get();
+    int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, socket_fd.Get(),
+                        &socket_event);
+    CHECK_EQ(ret, 0)
+        << "Endpoint::Endpoint: Failed to add socket fd to epoll fd: "
+        << strerror(errno);
+  }
+
+  epoll_event cancel_event;
+  cancel_event.events = EPOLLIN;
+  cancel_event.data.fd = cancel_event_fd_.Get();
+
+  int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, cancel_event_fd_.Get(),
+                      &cancel_event);
+  CHECK_EQ(ret, 0)
+      << "Endpoint::Endpoint: Failed to add cancel event fd to epoll fd: "
+      << strerror(errno);
+  socket_fd_ = std::move(socket_fd);
+}
+
+void* Endpoint::AllocateMessageState() { return new MessageState; }
+
+void Endpoint::FreeMessageState(void* state) {
+  delete static_cast<MessageState*>(state);
+}
+
+Status<void> Endpoint::AcceptConnection(Message* message) {
+  if (!socket_fd_)
+    return ErrorStatus(EBADF);
+
+  sockaddr_un remote;
+  socklen_t addrlen = sizeof(remote);
+  LocalHandle connection_fd{accept4(socket_fd_.Get(),
+                                    reinterpret_cast<sockaddr*>(&remote),
+                                    &addrlen, SOCK_CLOEXEC)};
+  if (!connection_fd) {
+    ALOGE("Endpoint::AcceptConnection: failed to accept connection: %s",
+          strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  LocalHandle local_socket;
+  LocalHandle remote_socket;
+  auto status = CreateChannelSocketPair(&local_socket, &remote_socket);
+  if (!status)
+    return status;
+
+  // Borrow the local channel handle before we move it into OnNewChannel().
+  BorrowedHandle channel_handle = local_socket.Borrow();
+  status = OnNewChannel(std::move(local_socket));
+  if (!status)
+    return status;
+
+  // Send the channel socket fd to the client.
+  ChannelConnectionInfo<LocalHandle> connection_info;
+  connection_info.channel_fd = std::move(remote_socket);
+  status = SendData(connection_fd.Borrow(), connection_info);
+
+  if (status) {
+    // Get the CHANNEL_OPEN message from client over the channel socket.
+    status = ReceiveMessageForChannel(channel_handle, message);
+  } else {
+    CloseChannel(GetChannelId(channel_handle));
+  }
+
+  // Don't need the connection socket anymore. Further communication should
+  // happen over the channel socket.
+  shutdown(connection_fd.Get(), SHUT_WR);
+  return status;
+}
+
+Status<void> Endpoint::SetService(Service* service) {
+  service_ = service;
+  return {};
+}
+
+Status<void> Endpoint::SetChannel(int channel_id, Channel* channel) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  auto channel_data = channels_.find(channel_id);
+  if (channel_data == channels_.end())
+    return ErrorStatus{EINVAL};
+  channel_data->second.channel_state = channel;
+  return {};
+}
+
+Status<void> Endpoint::OnNewChannel(LocalHandle channel_fd) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  Status<void> status;
+  status.PropagateError(OnNewChannelLocked(std::move(channel_fd), nullptr));
+  return status;
+}
+
+Status<std::pair<int32_t, Endpoint::ChannelData*>> Endpoint::OnNewChannelLocked(
+    LocalHandle channel_fd, Channel* channel_state) {
+  epoll_event event;
+  event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+  event.data.fd = channel_fd.Get();
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, channel_fd.Get(), &event) < 0) {
+    ALOGE(
+        "Endpoint::OnNewChannelLocked: Failed to add channel to endpoint: %s\n",
+        strerror(errno));
+    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 (;;) {
+    // Try new channel IDs until we find one which is not already in the map.
+    if (last_channel_id_++ == std::numeric_limits<int32_t>::max())
+      last_channel_id_ = 1;
+    auto iter = channels_.lower_bound(last_channel_id_);
+    if (iter == channels_.end() || iter->first != last_channel_id_) {
+      channel_fd_to_id_.emplace(channel_data.data_fd.Get(), last_channel_id_);
+      iter = channels_.emplace_hint(iter, last_channel_id_,
+                                    std::move(channel_data));
+      return std::make_pair(last_channel_id_, &iter->second);
+    }
+  }
+}
+
+Status<void> Endpoint::ReenableEpollEvent(const BorrowedHandle& fd) {
+  epoll_event event;
+  event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+  event.data.fd = fd.Get();
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, fd.Get(), &event) < 0) {
+    ALOGE(
+        "Endpoint::ReenableEpollEvent: Failed to re-enable channel to "
+        "endpoint: %s\n",
+        strerror(errno));
+    return ErrorStatus(errno);
+  }
+  return {};
+}
+
+Status<void> Endpoint::CloseChannel(int channel_id) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  return CloseChannelLocked(channel_id);
+}
+
+Status<void> Endpoint::CloseChannelLocked(int32_t channel_id) {
+  ALOGD_IF(TRACE, "Endpoint::CloseChannelLocked: channel_id=%d", channel_id);
+
+  auto iter = channels_.find(channel_id);
+  if (iter == channels_.end())
+    return ErrorStatus{EINVAL};
+
+  int channel_fd = iter->second.data_fd.Get();
+  Status<void> status;
+  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) {
+    status.SetError(errno);
+    ALOGE(
+        "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
+        "%s\n",
+        strerror(errno));
+  } else {
+    status.SetValue();
+  }
+
+  channel_fd_to_id_.erase(channel_fd);
+  channels_.erase(iter);
+  return status;
+}
+
+Status<void> Endpoint::ModifyChannelEvents(int channel_id, int clear_mask,
+                                           int set_mask) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+
+  auto search = channels_.find(channel_id);
+  if (search != channels_.end()) {
+    auto& channel_data = search->second;
+    channel_data.event_set.ModifyEvents(clear_mask, set_mask);
+    return {};
+  }
+
+  return ErrorStatus{EINVAL};
+}
+
+Status<void> Endpoint::CreateChannelSocketPair(LocalHandle* local_socket,
+                                               LocalHandle* remote_socket) {
+  Status<void> status;
+  char* endpoint_context = nullptr;
+  // Make sure the channel socket has the correct SELinux label applied.
+  // Here we get the label from the endpoint file descriptor, which should be
+  // something like "u:object_r:pdx_service_endpoint_socket:s0" and replace
+  // "endpoint" with "channel" to produce the channel label such as this:
+  // "u:object_r:pdx_service_channel_socket:s0".
+  if (fgetfilecon_raw(socket_fd_.Get(), &endpoint_context) > 0) {
+    std::string channel_context = endpoint_context;
+    freecon(endpoint_context);
+    const std::string suffix = "_endpoint_socket";
+    auto pos = channel_context.find(suffix);
+    if (pos != std::string::npos) {
+      channel_context.replace(pos, suffix.size(), "_channel_socket");
+    } else {
+      ALOGW(
+          "Endpoint::CreateChannelSocketPair: Endpoint security context '%s' "
+          "does not contain expected substring '%s'",
+          channel_context.c_str(), suffix.c_str());
+    }
+    ALOGE_IF(setsockcreatecon_raw(channel_context.c_str()) == -1,
+             "Endpoint::CreateChannelSocketPair: Failed to set channel socket "
+             "security context: %s",
+             strerror(errno));
+  } else {
+    ALOGE(
+        "Endpoint::CreateChannelSocketPair: Failed to obtain the endpoint "
+        "socket's security context: %s",
+        strerror(errno));
+  }
+
+  int channel_pair[2] = {};
+  if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_pair) == -1) {
+    ALOGE("Endpoint::CreateChannelSocketPair: Failed to create socket pair: %s",
+          strerror(errno));
+    status.SetError(errno);
+    return status;
+  }
+
+  setsockcreatecon_raw(nullptr);
+
+  local_socket->Reset(channel_pair[0]);
+  remote_socket->Reset(channel_pair[1]);
+
+  int optval = 1;
+  if (setsockopt(local_socket->Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+                 sizeof(optval)) == -1) {
+    ALOGE(
+        "Endpoint::CreateChannelSocketPair: Failed to enable the receiving of "
+        "the credentials for channel %d: %s",
+        local_socket->Get(), strerror(errno));
+    status.SetError(errno);
+  }
+  return status;
+}
+
+Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
+                                                  int /*flags*/,
+                                                  Channel* channel,
+                                                  int* channel_id) {
+  LocalHandle local_socket;
+  LocalHandle remote_socket;
+  auto status = CreateChannelSocketPair(&local_socket, &remote_socket);
+  if (!status)
+    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;
+
+  // 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());
+  if (!ref)
+    return ref.error_status();
+  state->sockets_to_close.push_back(std::move(remote_socket));
+  return RemoteChannelHandle{ref.get()};
+}
+
+Status<int> Endpoint::CheckChannel(const Message* /*message*/,
+                                   ChannelReference /*ref*/,
+                                   Channel** /*channel*/) {
+  // TODO(xiaohuit): Implement this.
+  return ErrorStatus(EFAULT);
+}
+
+Channel* Endpoint::GetChannelState(int32_t channel_id) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  auto channel_data = channels_.find(channel_id);
+  return (channel_data != channels_.end()) ? channel_data->second.channel_state
+                                           : nullptr;
+}
+
+BorrowedHandle Endpoint::GetChannelSocketFd(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.data_fd.Borrow();
+  return handle;
+}
+
+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;
+}
+
+int32_t Endpoint::GetChannelId(const BorrowedHandle& channel_fd) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  auto iter = channel_fd_to_id_.find(channel_fd.Get());
+  return (iter != channel_fd_to_id_.end()) ? iter->second : -1;
+}
+
+Status<void> Endpoint::ReceiveMessageForChannel(
+    const BorrowedHandle& channel_fd, Message* message) {
+  RequestHeader<LocalHandle> request;
+  int32_t channel_id = GetChannelId(channel_fd);
+  auto status = ReceiveData(channel_fd.Borrow(), &request);
+  if (!status) {
+    if (status.error() == ESHUTDOWN) {
+      BuildCloseMessage(channel_id, message);
+      return {};
+    } else {
+      CloseChannel(channel_id);
+      return status;
+    }
+  }
+
+  MessageInfo info;
+  info.pid = request.cred.pid;
+  info.tid = -1;
+  info.cid = channel_id;
+  info.mid = request.is_impulse ? Message::IMPULSE_MESSAGE_ID
+                                : GetNextAvailableMessageId();
+  info.euid = request.cred.uid;
+  info.egid = request.cred.gid;
+  info.op = request.op;
+  info.flags = 0;
+  info.service = service_;
+  info.channel = GetChannelState(channel_id);
+  info.send_len = request.send_len;
+  info.recv_len = request.max_recv_len;
+  info.fd_count = request.file_descriptors.size();
+  static_assert(sizeof(info.impulse) == request.impulse_payload.size(),
+                "Impulse payload sizes must be the same in RequestHeader and "
+                "MessageInfo");
+  memcpy(info.impulse, request.impulse_payload.data(),
+         request.impulse_payload.size());
+  *message = Message{info};
+  auto* state = static_cast<MessageState*>(message->GetState());
+  state->request = std::move(request);
+  if (request.send_len > 0 && !request.is_impulse) {
+    state->request_data.resize(request.send_len);
+    status = ReceiveData(channel_fd, state->request_data.data(),
+                         state->request_data.size());
+  }
+
+  if (status && request.is_impulse)
+    status = ReenableEpollEvent(channel_fd);
+
+  if (!status) {
+    if (status.error() == ESHUTDOWN) {
+      BuildCloseMessage(channel_id, message);
+      return {};
+    } else {
+      CloseChannel(channel_id);
+      return status;
+    }
+  }
+
+  return status;
+}
+
+void Endpoint::BuildCloseMessage(int32_t channel_id, Message* message) {
+  ALOGD_IF(TRACE, "Endpoint::BuildCloseMessage: channel_id=%d", channel_id);
+  MessageInfo info;
+  info.pid = -1;
+  info.tid = -1;
+  info.cid = channel_id;
+  info.mid = GetNextAvailableMessageId();
+  info.euid = -1;
+  info.egid = -1;
+  info.op = opcodes::CHANNEL_CLOSE;
+  info.flags = 0;
+  info.service = service_;
+  info.channel = GetChannelState(channel_id);
+  info.send_len = 0;
+  info.recv_len = 0;
+  info.fd_count = 0;
+  *message = Message{info};
+}
+
+Status<void> Endpoint::MessageReceive(Message* message) {
+  // Receive at most one event from the epoll set. This should prevent multiple
+  // dispatch threads from attempting to handle messages on the same socket at
+  // the same time.
+  epoll_event event;
+  int count = RETRY_EINTR(
+      epoll_wait(epoll_fd_.Get(), &event, 1, is_blocking_ ? -1 : 0));
+  if (count < 0) {
+    ALOGE("Endpoint::MessageReceive: Failed to wait for epoll events: %s\n",
+          strerror(errno));
+    return ErrorStatus{errno};
+  } else if (count == 0) {
+    return ErrorStatus{ETIMEDOUT};
+  }
+
+  if (event.data.fd == cancel_event_fd_.Get()) {
+    return ErrorStatus{ESHUTDOWN};
+  }
+
+  if (socket_fd_ && event.data.fd == socket_fd_.Get()) {
+    auto status = AcceptConnection(message);
+    if (!status)
+      return status;
+    return ReenableEpollEvent(socket_fd_.Borrow());
+  }
+
+  BorrowedHandle channel_fd{event.data.fd};
+  if (event.events & (EPOLLRDHUP | EPOLLHUP)) {
+    BuildCloseMessage(GetChannelId(channel_fd), message);
+    return {};
+  }
+
+  return ReceiveMessageForChannel(channel_fd, message);
+}
+
+Status<void> Endpoint::MessageReply(Message* message, int return_code) {
+  const int32_t channel_id = message->GetChannelId();
+  auto channel_socket = GetChannelSocketFd(channel_id);
+  if (!channel_socket)
+    return ErrorStatus{EBADF};
+
+  auto* state = static_cast<MessageState*>(message->GetState());
+  switch (message->GetOp()) {
+    case opcodes::CHANNEL_CLOSE:
+      return CloseChannel(channel_id);
+
+    case opcodes::CHANNEL_OPEN:
+      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();
+      }
+      break;
+  }
+
+  state->response.ret_code = return_code;
+  state->response.recv_len = state->response_data.size();
+  auto status = SendData(channel_socket, state->response);
+  if (status && !state->response_data.empty()) {
+    status = SendData(channel_socket, state->response_data.data(),
+                      state->response_data.size());
+  }
+
+  if (status)
+    status = ReenableEpollEvent(channel_socket);
+
+  return status;
+}
+
+Status<void> Endpoint::MessageReplyFd(Message* message, unsigned int push_fd) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  auto ref = state->PushFileHandle(BorrowedHandle{static_cast<int>(push_fd)});
+  if (!ref)
+    return ref.error_status();
+  return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+    Message* message, const LocalChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  auto ref = state->PushChannelHandle(handle.Borrow());
+  if (!ref)
+    return ref.error_status();
+  return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+    Message* message, const BorrowedChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  auto ref = state->PushChannelHandle(handle.Duplicate());
+  if (!ref)
+    return ref.error_status();
+  return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+    Message* message, const RemoteChannelHandle& handle) {
+  return MessageReply(message, handle.value());
+}
+
+Status<size_t> Endpoint::ReadMessageData(Message* message, const iovec* vector,
+                                         size_t vector_length) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->ReadData(vector, vector_length);
+}
+
+Status<size_t> Endpoint::WriteMessageData(Message* message, const iovec* vector,
+                                          size_t vector_length) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->WriteData(vector, vector_length);
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* message,
+                                               const LocalHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushFileHandle(handle.Borrow());
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* message,
+                                               const BorrowedHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushFileHandle(handle.Duplicate());
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* /*message*/,
+                                               const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+    Message* message, const LocalChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushChannelHandle(handle.Borrow());
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+    Message* message, const BorrowedChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushChannelHandle(handle.Duplicate());
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+    Message* /*message*/, const RemoteChannelHandle& handle) {
+  return handle.value();
+}
+
+LocalHandle Endpoint::GetFileHandle(Message* message, FileReference ref) const {
+  LocalHandle handle;
+  auto* state = static_cast<MessageState*>(message->GetState());
+  state->GetLocalFileHandle(ref, &handle);
+  return handle;
+}
+
+LocalChannelHandle Endpoint::GetChannelHandle(Message* message,
+                                              ChannelReference ref) const {
+  LocalChannelHandle handle;
+  auto* state = static_cast<MessageState*>(message->GetState());
+  state->GetLocalChannelHandle(ref, &handle);
+  return handle;
+}
+
+Status<void> Endpoint::Cancel() {
+  if (eventfd_write(cancel_event_fd_.Get(), 1) < 0)
+    return ErrorStatus{errno};
+  return {};
+}
+
+std::unique_ptr<Endpoint> Endpoint::Create(const std::string& endpoint_path,
+                                           mode_t /*unused_mode*/,
+                                           bool blocking) {
+  return std::unique_ptr<Endpoint>(new Endpoint(endpoint_path, blocking));
+}
+
+std::unique_ptr<Endpoint> Endpoint::CreateAndBindSocket(
+    const std::string& endpoint_path, bool blocking) {
+  return std::unique_ptr<Endpoint>(
+      new Endpoint(endpoint_path, blocking, false));
+}
+
+std::unique_ptr<Endpoint> Endpoint::CreateFromSocketFd(LocalHandle socket_fd) {
+  return std::unique_ptr<Endpoint>(new Endpoint(std::move(socket_fd)));
+}
+
+Status<void> Endpoint::RegisterNewChannelForTests(LocalHandle channel_fd) {
+  int optval = 1;
+  if (setsockopt(channel_fd.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+                 sizeof(optval)) == -1) {
+    ALOGE(
+        "Endpoint::RegisterNewChannelForTests: Failed to enable the receiving"
+        "of the credentials for channel %d: %s",
+        channel_fd.Get(), strerror(errno));
+    return ErrorStatus(errno);
+  }
+  return OnNewChannel(std::move(channel_fd));
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp
new file mode 100644
index 0000000..2943239
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_framework_tests.cpp
@@ -0,0 +1,688 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <array>
+#include <atomic>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/service.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;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ClientBase;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::uds::Endpoint;
+
+namespace {
+
+const size_t kLargeDataSize = 100000;
+
+const char kTestServicePath[] = "socket_test";
+const char kTestService1[] = "1";
+const char kTestService2[] = "2";
+
+enum test_op_codes {
+  TEST_OP_GET_SERVICE_ID,
+  TEST_OP_SET_TEST_CHANNEL,
+  TEST_OP_GET_THIS_CHANNEL_ID,
+  TEST_OP_GET_TEST_CHANNEL_ID,
+  TEST_OP_CHECK_CHANNEL_ID,
+  TEST_OP_CHECK_CHANNEL_OBJECT,
+  TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE,
+  TEST_OP_GET_NEW_CHANNEL,
+  TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE,
+  TEST_OP_GET_THIS_PROCESS_ID,
+  TEST_OP_GET_THIS_THREAD_ID,
+  TEST_OP_GET_THIS_EUID,
+  TEST_OP_GET_THIS_EGID,
+  TEST_OP_IMPULSE,
+  TEST_OP_POLLHUP_FROM_SERVICE,
+  TEST_OP_POLLIN_FROM_SERVICE,
+  TEST_OP_SEND_LARGE_DATA_RETURN_SUM,
+};
+
+using ImpulsePayload = std::array<std::uint8_t, sizeof(MessageInfo::impulse)>;
+
+// The test service creates a TestChannel for every client (channel) that
+// connects. This represents the service-side context for each client.
+class TestChannel : public Channel {
+ public:
+  explicit TestChannel(int channel_id) : channel_id_(channel_id) {}
+
+  int channel_id() const { return channel_id_; }
+
+ private:
+  friend class TestService;
+
+  int channel_id_;
+
+  TestChannel(const TestChannel&) = delete;
+  void operator=(const TestChannel&) = delete;
+};
+
+// Test service that creates a TestChannel for each channel and responds to test
+// messages.
+class TestService : public ServiceBase<TestService> {
+ public:
+  std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+    return std::make_shared<TestChannel>(message.GetChannelId());
+  }
+
+  void OnChannelClose(Message& /*message*/,
+                      const std::shared_ptr<Channel>& channel) override {
+    if (test_channel_ == channel)
+      test_channel_ = nullptr;
+  }
+
+  void HandleImpulse(Message& message) override {
+    switch (message.GetOp()) {
+      case TEST_OP_SET_TEST_CHANNEL:
+        test_channel_ = message.GetChannel<TestChannel>();
+        break;
+
+      case TEST_OP_IMPULSE: {
+        impulse_payload_.fill(0);
+        std::copy(message.ImpulseBegin(), message.ImpulseEnd(),
+                  impulse_payload_.begin());
+        break;
+      }
+
+      case TEST_OP_POLLHUP_FROM_SERVICE: {
+        message.ModifyChannelEvents(0, EPOLLHUP);
+        break;
+      }
+    }
+  }
+
+  Status<void> HandleMessage(Message& message) override {
+    switch (message.GetOp()) {
+      case TEST_OP_GET_SERVICE_ID:
+        REPLY_MESSAGE_RETURN(message, service_id_, {});
+
+      // Set the test channel to the TestChannel for the current channel. Other
+      // messages can use this to perform tests.
+      case TEST_OP_SET_TEST_CHANNEL:
+        test_channel_ = message.GetChannel<TestChannel>();
+        REPLY_MESSAGE_RETURN(message, 0, {});
+
+      // Return the channel id for the current channel.
+      case TEST_OP_GET_THIS_CHANNEL_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetChannelId(), {});
+
+      // Return the channel id for the test channel.
+      case TEST_OP_GET_TEST_CHANNEL_ID:
+        if (test_channel_)
+          REPLY_MESSAGE_RETURN(message, test_channel_->channel_id(), {});
+        else
+          REPLY_ERROR_RETURN(message, ENOENT, {});
+
+      // Test check channel feature.
+      case TEST_OP_CHECK_CHANNEL_ID: {
+        ChannelReference ref = 0;
+        if (!message.ReadAll(&ref, sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, {});
+
+        const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr);
+        REPLY_MESSAGE_RETURN(message, ret, {});
+      }
+
+      case TEST_OP_CHECK_CHANNEL_OBJECT: {
+        std::shared_ptr<TestChannel> channel;
+        ChannelReference ref = 0;
+        if (!message.ReadAll(&ref, sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, {});
+
+        const Status<int> ret =
+            message.CheckChannel<TestChannel>(ref, &channel);
+        if (!ret)
+          REPLY_MESSAGE_RETURN(message, ret, {});
+
+        if (channel != nullptr)
+          REPLY_MESSAGE_RETURN(message, channel->channel_id(), {});
+        else
+          REPLY_ERROR_RETURN(message, ENODATA, {});
+      }
+
+      case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: {
+        ChannelReference ref = 0;
+        if (!message.ReadAll(&ref, sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, {});
+
+        const Status<int> ret = message.CheckChannel<TestChannel>(
+            other_service_.get(), ref, nullptr);
+        REPLY_MESSAGE_RETURN(message, ret, {});
+      }
+
+      case TEST_OP_GET_NEW_CHANNEL: {
+        auto channel = std::make_shared<TestChannel>(-1);
+        Status<RemoteChannelHandle> channel_handle =
+            message.PushChannel(0, channel, &channel->channel_id_);
+        REPLY_MESSAGE_RETURN(message, channel_handle, {});
+      }
+
+      case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: {
+        if (!other_service_)
+          REPLY_ERROR_RETURN(message, EINVAL, {});
+
+        auto channel = std::make_shared<TestChannel>(-1);
+        Status<RemoteChannelHandle> channel_handle = message.PushChannel(
+            other_service_.get(), 0, channel, &channel->channel_id_);
+        REPLY_MESSAGE_RETURN(message, channel_handle, {});
+      }
+
+      case TEST_OP_GET_THIS_PROCESS_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetProcessId(), {});
+
+      case TEST_OP_GET_THIS_THREAD_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetThreadId(), {});
+
+      case TEST_OP_GET_THIS_EUID:
+        REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), {});
+
+      case TEST_OP_GET_THIS_EGID:
+        REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), {});
+
+      case TEST_OP_POLLIN_FROM_SERVICE:
+        REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN),
+                             {});
+
+      case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: {
+        std::array<int, kLargeDataSize> data_array;
+        size_t size_to_read = data_array.size() * sizeof(int);
+        if (!message.ReadAll(data_array.data(), size_to_read)) {
+          REPLY_ERROR_RETURN(message, EIO, {});
+        }
+        int sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+        REPLY_MESSAGE_RETURN(message, sum, {});
+      }
+
+      default:
+        return Service::DefaultHandleMessage(message);
+    }
+  }
+
+  const ImpulsePayload& GetImpulsePayload() const { return impulse_payload_; }
+
+ private:
+  friend BASE;
+
+  std::shared_ptr<TestChannel> test_channel_;
+  std::shared_ptr<TestService> other_service_;
+  int service_id_;
+  ImpulsePayload impulse_payload_;
+
+  static std::atomic<int> service_counter_;
+
+  TestService(const std::string& name,
+              const std::shared_ptr<TestService>& other_service)
+      : TestService(name, other_service, false) {}
+
+  TestService(const std::string& name,
+              const std::shared_ptr<TestService>& other_service, bool blocking)
+      : BASE(std::string("TestService") + name,
+             Endpoint::CreateAndBindSocket(kTestServicePath + name, blocking)),
+        other_service_(other_service),
+        service_id_(service_counter_++) {}
+
+  explicit TestService(const std::string& name) : TestService(name, nullptr) {}
+
+  TestService(const TestService&) = delete;
+  void operator=(const TestService&) = delete;
+};
+
+std::atomic<int> TestService::service_counter_;
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+  // Requests the service id of the service this channel is connected to.
+  int GetServiceId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_SERVICE_ID));
+  }
+
+  // Requests the test channel to be set to this client's channel.
+  int SetTestChannel() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_SET_TEST_CHANNEL));
+  }
+
+  // Request the test channel to be set to this client's channel using an async
+  // message.
+  int SetTestChannelAsync() {
+    return ReturnStatusOrError(SendImpulse(TEST_OP_SET_TEST_CHANNEL));
+  }
+
+  // Sends a test async message with payload.
+  int SendAsync(const void* buffer, size_t length) {
+    Transaction trans{*this};
+    return ReturnStatusOrError(SendImpulse(TEST_OP_IMPULSE, buffer, length));
+  }
+
+  // Requests the channel id for this client.
+  int GetThisChannelId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_CHANNEL_ID));
+  }
+
+  // Requests the channel id of the test channel.
+  int GetTestChannelId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_TEST_CHANNEL_ID));
+  }
+
+  // Checks whether the fd |channel_id| is a channel to the test service.
+  // Returns the channel id of the channel.
+  int CheckChannelIdArgument(BorrowedChannelHandle channel) {
+    Transaction trans{*this};
+    ChannelReference ref = trans.PushChannelHandle(channel).get();
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_ID, &ref,
+                                               sizeof(ref), nullptr, 0));
+  }
+
+  // Checks whether the fd |channel_id| is a channel to the test service.
+  // Returns the channel id of the channel exercising the context pointer.
+  int CheckChannelObjectArgument(BorrowedChannelHandle channel) {
+    Transaction trans{*this};
+    ChannelReference ref = trans.PushChannelHandle(channel).get();
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_OBJECT,
+                                               &ref, sizeof(ref), nullptr, 0));
+  }
+
+  // Checks whether the fd |channel_fd| is a channel to the other test service.
+  // Returns 0 on success.
+  int CheckChannelFromOtherService(BorrowedChannelHandle channel) {
+    Transaction trans{*this};
+    ChannelReference ref = trans.PushChannelHandle(channel).get();
+    return ReturnStatusOrError(
+        trans.Send<int>(TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, &ref,
+                        sizeof(ref), nullptr, 0));
+  }
+
+  // Requests a new channel to the service.
+  std::unique_ptr<TestClient> GetNewChannel() {
+    Transaction trans{*this};
+    auto status = trans.Send<LocalChannelHandle>(TEST_OP_GET_NEW_CHANNEL);
+    if (status)
+      return TestClient::Create(status.take());
+    else
+      return nullptr;
+  }
+
+  // Requests a new channel to the other service.
+  std::unique_ptr<TestClient> GetNewChannelFromOtherService() {
+    Transaction trans{*this};
+    auto status = trans.Send<LocalChannelHandle>(
+        TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE);
+    if (status)
+      return TestClient::Create(status.take());
+    else
+      return nullptr;
+  }
+
+  // Requests an id from the message description.
+  pid_t GetThisProcessId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_PROCESS_ID));
+  }
+  pid_t GetThisThreadId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_THREAD_ID));
+  }
+  uid_t GetThisEffectiveUserId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EUID));
+  }
+  gid_t GetThisEffectiveGroupId() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EGID));
+  }
+
+  int SendPollHupEvent() {
+    return ReturnStatusOrError(SendImpulse(TEST_OP_POLLHUP_FROM_SERVICE));
+  }
+
+  int SendPollInEvent() {
+    Transaction trans{*this};
+    return ReturnStatusOrError(trans.Send<int>(TEST_OP_POLLIN_FROM_SERVICE));
+  }
+
+  int SendLargeDataReturnSum(
+      const std::array<int, kLargeDataSize>& data_array) {
+    Transaction trans{*this};
+    return ReturnStatusOrError(
+        trans.Send<int>(TEST_OP_SEND_LARGE_DATA_RETURN_SUM, data_array.data(),
+                        data_array.size() * sizeof(int), nullptr, 0));
+  }
+
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return ErrorStatus(EINVAL);
+    }
+  }
+
+  using ClientBase<TestClient>::event_fd;
+
+  enum : size_t { kMaxPayload = MAX_IMPULSE_LENGTH };
+
+ private:
+  friend BASE;
+
+  explicit TestClient(const std::string& name)
+      : BASE{android::pdx::uds::ClientChannelFactory::Create(kTestServicePath +
+                                                             name)} {}
+
+  explicit TestClient(LocalChannelHandle channel)
+      : BASE{android::pdx::uds::ClientChannel::Create(std::move(channel))} {}
+
+  TestClient(const TestClient&) = delete;
+  void operator=(const TestClient&) = delete;
+};
+
+}  // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. These objects are cleaned up in the same
+// thread, order is important; either the service or the client must be
+// destroyed before the dispatcher is stopped. The reason for this is that
+// clients send blocking "close" messages to their respective services on
+// destruction. If this happens after the dispatcher is stopped the client
+// destructor will get blocked waiting for a reply that will never come. In
+// normal use of the service framework this is never an issue because clients
+// and the dispatcher for the same service are never destructed in the same
+// thread (they live in different processes).
+class ServiceFrameworkTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<ServiceDispatcher> dispatcher_;
+  std::thread dispatch_thread_;
+
+  void SetUp() override {
+    // Create a dispatcher to handle messages to services.
+    dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+    ASSERT_NE(nullptr, dispatcher_);
+
+    // Start the message dispatch loop in a separate thread.
+    dispatch_thread_ = std::thread(
+        std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+  }
+
+  void TearDown() override {
+    if (dispatcher_) {
+      // Cancel the dispatcher and wait for the thread to terminate. Explicitly
+      // join the thread so that destruction doesn't deallocate the dispatcher
+      // before the thread finishes.
+      dispatcher_->SetCanceled(true);
+      dispatch_thread_.join();
+    }
+  }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(ServiceFrameworkTest, BasicClientService) {
+  // 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));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  // Get the channel id that will be returned by the next tests.
+  const int channel_id = client->GetThisChannelId();
+  EXPECT_LE(0, channel_id);
+
+  // Check return value before test channel is set.
+  EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+  // Set test channel and perform the test again.
+  EXPECT_EQ(0, client->SetTestChannel());
+  EXPECT_EQ(channel_id, client->GetTestChannelId());
+}
+
+// Test impulses.
+TEST_F(ServiceFrameworkTest, Impulse) {
+  // 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);
+
+  // Get the channel id that will be returned by the next tests.
+  const int channel_id = client->GetThisChannelId();
+  EXPECT_LE(0, channel_id);
+
+  // Check return value before test channel is set.
+  EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+  // Set test channel with an impulse and perform the test again.
+  EXPECT_EQ(0, client->SetTestChannelAsync());
+  EXPECT_EQ(channel_id, client->GetTestChannelId());
+
+  ImpulsePayload expected_payload = {{'a', 'b', 'c'}};
+  EXPECT_EQ(0, client->SendAsync(expected_payload.data(), 3));
+  // Send a synchronous message to make sure the async message is handled before
+  // we check the payload.
+  client->GetThisChannelId();
+  EXPECT_EQ(expected_payload, service->GetImpulsePayload());
+
+  // Impulse payloads are limited to 4 machine words.
+  EXPECT_EQ(
+      0, client->SendAsync(expected_payload.data(), TestClient::kMaxPayload));
+  EXPECT_EQ(-EINVAL, client->SendAsync(expected_payload.data(),
+                                       TestClient::kMaxPayload + 1));
+
+  // Test invalid pointer.
+  const std::uint8_t* invalid_pointer = nullptr;
+  EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int)));
+}
+
+// Test Message::PushChannel/Service::PushChannel API.
+TEST_F(ServiceFrameworkTest, PushChannel) {
+  // Create a test service and add it to the dispatcher.
+  auto other_service = TestService::Create(kTestService1);
+  ASSERT_NE(nullptr, other_service);
+  ASSERT_EQ(0, dispatcher_->AddService(other_service));
+
+  // Create a second test service and add it to the dispatcher.
+  auto service = TestService::Create(kTestService2, other_service);
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to the second test service.
+  auto client1 = TestClient::Create(kTestService2);
+  ASSERT_NE(nullptr, client1);
+
+  // Test the creation of new channels using the push APIs.
+  const int channel_id1 = client1->GetThisChannelId();
+  EXPECT_LE(0, channel_id1);
+
+  auto client2 = client1->GetNewChannel();
+  EXPECT_NE(nullptr, client2);
+  EXPECT_NE(client1->event_fd(), client2->event_fd());
+
+  const int channel_id2 = client2->GetThisChannelId();
+  EXPECT_LE(0, channel_id2);
+  EXPECT_NE(channel_id1, channel_id2);
+
+  auto client3 = client1->GetNewChannelFromOtherService();
+  EXPECT_NE(nullptr, client3);
+  EXPECT_NE(client1->event_fd(), client3->event_fd());
+
+  const int channel_id3 = client3->GetThisChannelId();
+  EXPECT_LE(0, channel_id3);
+
+  // Test which services the channels are connected to.
+  const int service_id1 = client1->GetServiceId();
+  EXPECT_LE(0, service_id1);
+
+  const int service_id2 = client2->GetServiceId();
+  EXPECT_LE(0, service_id2);
+
+  const int service_id3 = client3->GetServiceId();
+  EXPECT_LE(0, service_id3);
+
+  EXPECT_EQ(service_id1, service_id2);
+  EXPECT_NE(service_id1, service_id3);
+}
+
+// Tests process id, thread id, effective user id, and effective group id
+// returned in the message description.
+TEST_F(ServiceFrameworkTest, Ids) {
+  // 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));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  // Pids 0-2 are defined, no user task should have them.
+
+  const pid_t process_id1 = client->GetThisProcessId();
+  EXPECT_LT(2, process_id1);
+
+  pid_t process_id2;
+
+  std::thread thread([&]() {
+    process_id2 = client->GetThisProcessId();
+  });
+  thread.join();
+
+  EXPECT_LT(2, process_id2);
+  EXPECT_EQ(process_id1, process_id2);
+
+  // This test must run as root for the rest of these tests to work.
+  const int euid1 = client->GetThisEffectiveUserId();
+  ASSERT_EQ(0, euid1);
+
+  const int egid1 = client->GetThisEffectiveGroupId();
+  EXPECT_EQ(0, egid1);
+
+  // Set effective uid/gid to system.
+  ASSERT_EQ(0, setegid(AID_SYSTEM));
+  ASSERT_EQ(0, seteuid(AID_SYSTEM));
+
+  const int euid2 = client->GetThisEffectiveUserId();
+  EXPECT_EQ(AID_SYSTEM, euid2);
+
+  const int egid2 = client->GetThisEffectiveGroupId();
+  EXPECT_EQ(AID_SYSTEM, egid2);
+
+  // Set the euid/egid back to root.
+  ASSERT_EQ(0, setegid(0));
+  ASSERT_EQ(0, seteuid(0));
+}
+
+TEST_F(ServiceFrameworkTest, PollIn) {
+  // 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));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  epoll_event event;
+  int count = epoll_wait(client->event_fd(), &event, 1, 0);
+  ASSERT_EQ(0, count);
+
+  client->SendPollInEvent();
+
+  count = epoll_wait(client->event_fd(), &event, 1, -1);
+  ASSERT_EQ(1, count);
+  ASSERT_TRUE((EPOLLIN & event.events) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, PollHup) {
+  // 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));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  epoll_event event;
+  int count = epoll_wait(client->event_fd(), &event, 1, 0);
+  ASSERT_EQ(0, count);
+
+  client->SendPollHupEvent();
+
+  count = epoll_wait(client->event_fd(), &event, 1, -1);
+  ASSERT_EQ(1, count);
+  auto event_status = client->GetEventMask(event.events);
+  ASSERT_TRUE(event_status.ok());
+  ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, LargeDataSum) {
+  // 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));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  std::array<int, kLargeDataSize> data_array;
+  std::iota(data_array.begin(), data_array.end(), 0);
+  int expected_sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+  int sum = client->SendLargeDataReturnSum(data_array);
+  ASSERT_EQ(expected_sum, sum);
+}
+
+TEST_F(ServiceFrameworkTest, Cancel) {
+  // Create a test service and add it to the dispatcher.
+  auto service = TestService::Create(kTestService1, nullptr, true);
+  ASSERT_NE(nullptr, service);
+  ASSERT_EQ(0, dispatcher_->AddService(service));
+
+  // Create a client to service.
+  auto client = TestClient::Create(kTestService1);
+  ASSERT_NE(nullptr, client);
+
+  auto previous_time = std::chrono::system_clock::now();
+  dispatcher_->ReceiveAndDispatch(100);  // 0.1 seconds should block.
+  auto time = std::chrono::system_clock::now();
+  ASSERT_LE(100, std::chrono::duration_cast<std::chrono::milliseconds>(
+                     time - previous_time)
+                     .count());
+  service->Cancel();
+  // Non-blocking. Return immediately.
+  dispatcher_->ReceiveAndDispatch(-1);
+  dispatcher_->ReceiveAndDispatch(-1);
+}
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
new file mode 100644
index 0000000..364873d
--- /dev/null
+++ b/libs/vr/libperformance/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "performance_client.cpp",
+    "performance_rpc.cpp",
+]
+
+includeFiles = [ "include" ]
+
+staticLibraries = ["libpdx_default_transport"]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libutils",
+]
+
+cc_library {
+    srcs: sourceFiles,
+    cflags: [
+        "-DLOG_TAG=\"libperformance\"",
+	"-DTRACE=0"
+    ],
+    export_include_dirs: includeFiles,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "libperformance",
+}
diff --git a/libs/vr/libperformance/include/CPPLINT.cfg b/libs/vr/libperformance/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libperformance/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h
new file mode 100644
index 0000000..2216e38
--- /dev/null
+++ b/libs/vr/libperformance/include/dvr/performance_client_api.h
@@ -0,0 +1,59 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+
+#include <stddef.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Sets the CPU partition for a task.
+///
+/// Sets the CPU partition for a task to the partition described by a CPU
+/// partition path.
+///
+/// TODO(eieio): Describe supported partitions and rules governing assignment.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param partition NULL-terminated ASCII string describing the CPU partition
+/// to attach the task to.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetCpuPartition(pid_t task_id, const char* partition);
+
+/// Sets the scheduler class for a task.
+///
+/// Sets the scheduler class for a task to the class described by a semantic
+/// string.
+///
+/// Supported classes for applications are: audio, graphics, normal, and
+/// background. Additional options following a ':' to be supported in the
+/// future.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param scheduler_class NULL-terminated ASCII string containing the desired
+/// scheduler class.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetSchedulerClass(pid_t task_id, const char* scheduler_class);
+
+/// Gets the CPU partition for a task.
+///
+/// Gets the CPU partition path for a task as a NULL-terminated ASCII string. If
+/// the path is too large to fit in the supplied buffer, -ENOBUFS is returned.
+///
+/// @param task_id The task id of the task to retrieve the partition for. When
+/// task_id is 0 the current task id is substituted.
+/// @param partition Pointer to an ASCII string buffer to store the partition
+/// path.
+/// @param size Size of the string buffer in bytes, including space for the NULL
+/// terminator.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h
new file mode 100644
index 0000000..a61c6b2
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_client.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_H_
+
+#include <sys/types.h>
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/client.h>
+
+namespace android {
+namespace dvr {
+
+class PerformanceClient : public pdx::ClientBase<PerformanceClient> {
+ public:
+  int SetCpuPartition(pid_t task_id, const std::string& partition);
+  int SetCpuPartition(pid_t task_id, const char* partition);
+  int SetSchedulerClass(pid_t task_id, const std::string& scheduler_class);
+  int SetSchedulerClass(pid_t task_id, const char* scheduler_class);
+  int GetCpuPartition(pid_t task_id, std::string* partition_out);
+  int GetCpuPartition(pid_t task_id, char* partition_out, std::size_t size);
+
+ private:
+  friend BASE;
+
+  explicit PerformanceClient(int* error);
+
+  PerformanceClient(const PerformanceClient&) = delete;
+  void operator=(const PerformanceClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCE_CLIENT_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
new file mode 100644
index 0000000..73bdaa7
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_PERFORMANCE_RPC_H_
+#define ANDROID_DVR_PERFORMANCE_RPC_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <pdx/rpc/remote_method_type.h>
+
+namespace android {
+namespace dvr {
+
+// Performance Service RPC interface. Defines the endpoint paths, op codes, and
+// method type signatures supported by performanced.
+struct PerformanceRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/performance/client";
+
+  // Op codes.
+  enum {
+    kOpSetCpuPartition = 0,
+    kOpSetSchedulerClass,
+    kOpGetCpuPartition,
+  };
+
+  // Methods.
+  PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition,
+                    int(pid_t, const std::string&));
+  PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass,
+                    int(pid_t, const std::string&));
+  PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCE_RPC_H_
diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp
new file mode 100644
index 0000000..2124162
--- /dev/null
+++ b/libs/vr/libperformance/performance_client.cpp
@@ -0,0 +1,119 @@
+#include "include/private/dvr/performance_client.h"
+
+#include <sys/types.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <private/dvr/performance_rpc.h>
+
+using android::pdx::rpc::WrapString;
+
+namespace android {
+namespace dvr {
+
+PerformanceClient::PerformanceClient(int* error)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          PerformanceRPC::kClientPath)) {
+  if (error)
+    *error = Client::error();
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id,
+                                       const std::string& partition) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(task_id, partition));
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id, const char* partition) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(
+          task_id, WrapString(partition)));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+                                         const std::string& scheduler_class) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(task_id,
+                                                            scheduler_class));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+                                         const char* scheduler_class) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+          task_id, WrapString(scheduler_class)));
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id,
+                                       std::string* partition_out) {
+  if (partition_out == nullptr)
+    return -EINVAL;
+
+  if (task_id == 0)
+    task_id = gettid();
+
+  auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+      partition_out, task_id);
+  return status ? 0 : -status.error();
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id, char* partition_out,
+                                       std::size_t size) {
+  if (partition_out == nullptr)
+    return -EINVAL;
+
+  if (task_id == 0)
+    task_id = gettid();
+
+  auto wrapper = WrapString(partition_out, size);
+  auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+      &wrapper, task_id);
+  if (!status)
+    return -status.error();
+
+  if (wrapper.size() < size)
+    partition_out[wrapper.size()] = '\0';
+
+  return 0;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+extern "C" int dvrSetCpuPartition(pid_t task_id, const char* partition) {
+  int error;
+  if (auto client = android::dvr::PerformanceClient::Create(&error))
+    return client->SetCpuPartition(task_id, partition);
+  else
+    return error;
+}
+
+extern "C" int dvrSetSchedulerClass(pid_t task_id,
+                                    const char* scheduler_class) {
+  int error;
+  if (auto client = android::dvr::PerformanceClient::Create(&error))
+    return client->SetSchedulerClass(task_id, scheduler_class);
+  else
+    return error;
+}
+
+extern "C" int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size) {
+  int error;
+  if (auto client = android::dvr::PerformanceClient::Create(&error))
+    return client->GetCpuPartition(task_id, partition, size);
+  else
+    return error;
+}
diff --git a/libs/vr/libperformance/performance_rpc.cpp b/libs/vr/libperformance/performance_rpc.cpp
new file mode 100644
index 0000000..9135349
--- /dev/null
+++ b/libs/vr/libperformance/performance_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/performance_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char PerformanceRPC::kClientPath[];
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvr_manager/Android.bp b/libs/vr/libvr_manager/Android.bp
new file mode 100644
index 0000000..8784877
--- /dev/null
+++ b/libs/vr/libvr_manager/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+exported_include_dirs = [ "include" ]
+
+include_dirs = ["frameworks/native/include/vr/vr_manager"]
+
+src_files = [
+    "vr_manager.cpp",
+    "trusted_uids.cpp",
+]
+
+static_libs = [
+    "libutils",
+    "libbinder",
+]
+
+cc_library_static {
+    srcs: src_files,
+    include_dirs: include_dirs,
+    export_include_dirs: exported_include_dirs,
+    cflags: ["-Wall", "-Werror", "-Wunused", "-Wunreachable-code"],
+    static_libs: static_libs,
+    name: "libvr_manager",
+}
diff --git a/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
new file mode 100644
index 0000000..4496fbf
--- /dev/null
+++ b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_TRUSTED_UIDS_H_
+#define ANDROID_DVR_TRUSTED_UIDS_H_
+
+#include <sys/types.h>
+
+namespace android {
+namespace dvr {
+
+/**
+ * Tells if a provided UID can be trusted to access restricted VR APIs.
+ *
+ * UID trust is based on the android.permission.RESTRICTED_VR_ACCESS permission.
+ * AID_SYSTEM and AID_ROOT are automatically trusted by Android.
+ *
+ * UIDs are guaranteed not to be reused until the next reboot even in case
+ * of package reinstall. For performance reasons this method caches results by
+ * default, as otherwise every check would trigger a Java call.
+ *
+ * This function is thread-safe.
+ *
+ * @param uid The uid to check.
+ * @param use_cache If true any cached result for the provided uid will be
+ *     reused. If false this call will reach the Application Manager Service
+ *     in Java to get updated values. Any updates will be stored in the cache.
+ * @return true if the uid is trusted, false if not or if the VR Manager Service
+ *         could not be reached to verify the uid.
+ */
+bool IsTrustedUid(uid_t uid, bool use_cache = true);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_TRUSTED_UIDS_H_
diff --git a/libs/vr/libvr_manager/trusted_uids.cpp b/libs/vr/libvr_manager/trusted_uids.cpp
new file mode 100644
index 0000000..4228a05
--- /dev/null
+++ b/libs/vr/libvr_manager/trusted_uids.cpp
@@ -0,0 +1,51 @@
+#include "private/dvr/trusted_uids.h"
+
+#include <mutex>
+#include <unordered_map>
+
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String16.h>
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+namespace dvr {
+
+bool IsTrustedUid(uid_t uid, bool use_cache) {
+  static std::unordered_map<uid_t, bool> uid_cache;
+  static std::mutex uid_cache_mutex;
+
+  // Whitelist requests from the system UID.
+  // These are already whitelisted by the permission service, but it might not
+  // be available if the ActivityManagerService is up during boot.
+  // This ensures the correct result for system services while booting up.
+  if (uid == AID_SYSTEM)
+    return true;
+
+  std::lock_guard<std::mutex> lock(uid_cache_mutex);
+
+  if (use_cache) {
+    auto it = uid_cache.find(uid);
+    if (it != uid_cache.end())
+      return it->second;
+  }
+
+  sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+  if (binder == 0) {
+    ALOGW("Could not access permission service");
+    return false;
+  }
+
+  // Note: we ignore the pid because it's only used to automatically reply
+  // true if the caller is the Activity Manager Service.
+  bool trusted = interface_cast<IPermissionController>(binder)->checkPermission(
+      String16("android.permission.RESTRICTED_VR_ACCESS"), -1, uid);
+
+  // Cache the information for this uid to avoid future Java calls.
+  uid_cache[uid] = trusted;
+  return trusted;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvr_manager/vr_manager.cpp b/libs/vr/libvr_manager/vr_manager.cpp
new file mode 100644
index 0000000..5cfc22e
--- /dev/null
+++ b/libs/vr/libvr_manager/vr_manager.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VrManager"
+#include <utils/Log.h>
+
+#include <vr/vr_manager/vr_manager.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class BpVrStateCallbacks : public BpInterface<IVrStateCallbacks> {
+ public:
+  explicit BpVrStateCallbacks(const sp<IBinder>& impl)
+      : BpInterface<IVrStateCallbacks>(impl) {}
+
+  void onVrStateChanged(bool enabled) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IVrStateCallbacks::getInterfaceDescriptor());
+    data.writeBool(enabled);
+    remote()->transact(ON_VR_STATE_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VrStateCallbacks, "android.service.vr.IVrStateCallbacks");
+
+status_t BnVrStateCallbacks::onTransact(uint32_t code, const Parcel& data,
+                                        Parcel* reply, uint32_t flags) {
+  switch(code) {
+    case ON_VR_STATE_CHANGED: {
+      CHECK_INTERFACE(IVrStateCallbacks, data, reply);
+      onVrStateChanged(data.readBool());
+      return OK;
+    }
+  }
+  return BBinder::onTransact(code, data, reply, flags);
+}
+
+// Must be kept in sync with interface defined in
+// IPersistentVrStateCallbacks.aidl.
+
+class BpPersistentVrStateCallbacks
+    : public BpInterface<IPersistentVrStateCallbacks> {
+ public:
+  explicit BpPersistentVrStateCallbacks(const sp<IBinder>& impl)
+      : BpInterface<IPersistentVrStateCallbacks>(impl) {}
+
+  void onPersistentVrStateChanged(bool enabled) {
+    Parcel data, reply;
+    data.writeInterfaceToken(
+        IPersistentVrStateCallbacks::getInterfaceDescriptor());
+    data.writeBool(enabled);
+    remote()->transact(ON_PERSISTENT_VR_STATE_CHANGED,
+                       data, &reply, IBinder::FLAG_ONEWAY);
+  }
+};
+
+IMPLEMENT_META_INTERFACE(PersistentVrStateCallbacks,
+                         "android.service.vr.IPersistentVrStateCallbacks");
+
+status_t BnPersistentVrStateCallbacks::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+  switch(code) {
+    case ON_PERSISTENT_VR_STATE_CHANGED: {
+      CHECK_INTERFACE(IPersistentVrStateCallbacks, data, reply);
+      onPersistentVrStateChanged(data.readBool());
+      return OK;
+    }
+  }
+  return BBinder::onTransact(code, data, reply, flags);
+}
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class BpVrManager : public BpInterface<IVrManager> {
+ public:
+  explicit BpVrManager(const sp<IBinder>& impl)
+      : BpInterface<IVrManager>(impl) {}
+
+  void registerListener(const sp<IVrStateCallbacks>& cb) override {
+    Parcel data;
+    data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+    data.writeStrongBinder(IInterface::asBinder(cb));
+    remote()->transact(REGISTER_LISTENER, data, NULL);
+  }
+
+  void unregisterListener(const sp<IVrStateCallbacks>& cb) override {
+    Parcel data;
+    data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+    data.writeStrongBinder(IInterface::asBinder(cb));
+    remote()->transact(UNREGISTER_LISTENER, data, NULL);
+  }
+
+  void registerPersistentVrStateListener(
+      const sp<IPersistentVrStateCallbacks>& cb) override {
+    Parcel data;
+    data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+    data.writeStrongBinder(IInterface::asBinder(cb));
+    remote()->transact(REGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL);
+  }
+
+  void unregisterPersistentVrStateListener(
+      const sp<IPersistentVrStateCallbacks>& cb) override {
+    Parcel data;
+    data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+    data.writeStrongBinder(IInterface::asBinder(cb));
+    remote()->transact(UNREGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL);
+  }
+
+  bool getVrModeState() override {
+    Parcel data, reply;
+    data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+    remote()->transact(GET_VR_MODE_STATE, data, &reply);
+    int32_t ret = reply.readExceptionCode();
+    if (ret != 0) {
+      return false;
+    }
+    return reply.readBool();
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VrManager, "android.service.vr.IVrManager");
+
+}  // namespace android
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
new file mode 100644
index 0000000..de26a74
--- /dev/null
+++ b/libs/vr/libvrflinger/Android.bp
@@ -0,0 +1,78 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "acquired_buffer.cpp",
+    "epoll_event_dispatcher.cpp",
+    "display_manager_service.cpp",
+    "display_service.cpp",
+    "display_surface.cpp",
+    "hardware_composer.cpp",
+    "vr_flinger.cpp",
+    "vsync_service.cpp",
+]
+
+includeFiles = [ "include" ]
+
+staticLibraries = [
+    "libsurfaceflingerincludes",
+    "libhwcomposer-command-buffer",
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libdisplay",
+    "libdvrcommon",
+    "libperformance",
+    "libvrsensor",
+    "libpdx_default_transport",
+    "libvr_manager",
+]
+
+sharedLibraries = [
+    "android.frameworks.vr.composer@1.0",
+    "android.hardware.graphics.allocator@2.0",
+    "android.hardware.graphics.composer@2.1",
+    "libbinder",
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libhardware",
+    "libnativewindow",
+    "libutils",
+    "libEGL",
+    "libGLESv1_CM",
+    "libGLESv2",
+    "libvulkan",
+    "libui",
+    "libgui",
+    "libsync",
+    "libhidlbase",
+    "libhidltransport",
+    "libfmq",
+]
+
+cc_library_static {
+    srcs: sourceFiles,
+    export_include_dirs: includeFiles,
+
+    cflags: [
+        "-DLOG_TAG=\"vr_flinger\"",
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    shared_libs: sharedLibraries,
+    whole_static_libs: staticLibraries,
+    name: "libvrflinger",
+}
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
new file mode 100644
index 0000000..7932a9c
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -0,0 +1,100 @@
+#include "acquired_buffer.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                               LocalHandle acquire_fence)
+    : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {}
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                               int* error) {
+  LocalHandle fence;
+  const int ret = buffer->Acquire(&fence);
+
+  if (error)
+    *error = ret;
+
+  if (ret < 0) {
+    ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
+          strerror(-ret));
+    buffer_ = nullptr;
+    // Default construct sets acquire_fence_ to empty.
+  } else {
+    buffer_ = buffer;
+    acquire_fence_ = std::move(fence);
+  }
+}
+
+AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other)
+    : buffer_(std::move(other.buffer_)),
+      acquire_fence_(std::move(other.acquire_fence_)) {}
+
+AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
+
+AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) {
+  if (this != &other) {
+    Release(LocalHandle(kEmptyFence));
+
+    buffer_ = std::move(other.buffer_);
+    acquire_fence_ = std::move(other.acquire_fence_);
+  }
+  return *this;
+}
+
+bool AcquiredBuffer::IsAvailable() const {
+  if (IsEmpty())
+    return false;
+
+  // Only check the fence if the acquire fence is not empty.
+  if (acquire_fence_) {
+    const int ret = sync_wait(acquire_fence_.Get(), 0);
+    ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
+             "AcquiredBuffer::IsAvailable: acquire_fence_=%d sync_wait()=%d "
+             "errno=%d.",
+             acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
+    if (ret == 0) {
+      // The fence is completed, so to avoid further calls to sync_wait we close
+      // it here.
+      acquire_fence_.Close();
+    }
+    return ret == 0;
+  } else {
+    return true;
+  }
+}
+
+LocalHandle AcquiredBuffer::ClaimAcquireFence() {
+  return std::move(acquire_fence_);
+}
+
+std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() {
+  return std::move(buffer_);
+}
+
+int AcquiredBuffer::Release(LocalHandle release_fence) {
+  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",
+            buffer_->id(), strerror(-ret));
+      if (ret != -ESHUTDOWN)
+        return ret;
+    }
+
+    buffer_ = nullptr;
+    acquire_fence_.Close();
+  }
+
+  return 0;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
new file mode 100644
index 0000000..dd4fcc5
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer.
+class AcquiredBuffer {
+ public:
+  static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
+
+  AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
+
+  // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire
+  // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling
+  // this constructor; the constructor does not attempt to ACQUIRE the buffer
+  // itself.
+  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                 pdx::LocalHandle acquire_fence);
+
+  // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
+  // be in the POSTED state prior to calling this constructor, as this
+  // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
+  // this instance is left in the empty state. An optional error code is
+  // returned in |error|, which may be nullptr if not needed.
+  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error);
+
+  // Move constructor. Behaves similarly to the move assignment operator below.
+  AcquiredBuffer(AcquiredBuffer&& other);
+
+  ~AcquiredBuffer();
+
+  // Move assignment operator. Moves the BufferConsumer and acquire fence from
+  // |other| into this instance after RELEASING the current BufferConsumer and
+  // closing the acquire fence. After the move |other| is left in the empty
+  // state.
+  AcquiredBuffer& operator=(AcquiredBuffer&& other);
+
+  // Accessors for the underlying BufferConsumer, the acquire fence, and the
+  // use-case specific sequence value from the acquisition (see
+  // private/dvr/buffer_hub_client.h).
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+
+  // When non-empty, returns true if the acquired fence was signaled (or if the
+  // fence is empty). Returns false when empty or if the fence is not signaled.
+  bool IsAvailable() const;
+
+  bool IsEmpty() const { return buffer_ == nullptr; }
+
+  // Returns the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence();
+
+  // Returns the buffer, passing ownership to the caller. Caller is responsible
+  // for calling Release on the returned buffer.
+  std::shared_ptr<BufferConsumer> ClaimBuffer();
+
+  // Releases the BufferConsumer, passing the release fence in |release_fence|
+  // 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);
+
+ private:
+  AcquiredBuffer(const AcquiredBuffer&) = delete;
+  void operator=(const AcquiredBuffer&) = delete;
+
+  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_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
new file mode 100644
index 0000000..a0b3efe
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -0,0 +1,155 @@
+#include "display_manager_service.h"
+
+#include <pdx/channel_handle.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/android_filesystem_config.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/trusted_uids.h>
+#include <sys/poll.h>
+
+#include <array>
+
+using android::dvr::display::DisplayManagerProtocol;
+using android::pdx::Channel;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::ErrorStatus;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+using android::pdx::rpc::RemoteMethodError;
+
+namespace android {
+namespace dvr {
+
+void DisplayManager::SetNotificationsPending(bool pending) {
+  auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
+                                              pending ? POLLIN : 0);
+  ALOGE_IF(!status,
+           "DisplayManager::SetNotificationPending: Failed to modify channel "
+           "events: %s",
+           status.GetErrorMessage().c_str());
+}
+
+DisplayManagerService::DisplayManagerService(
+    const std::shared_ptr<DisplayService>& display_service)
+    : BASE("DisplayManagerService",
+           Endpoint::Create(DisplayManagerProtocol::kClientPath)),
+      display_service_(display_service) {
+  display_service_->SetDisplayConfigurationUpdateNotifier(
+      std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
+}
+
+std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
+    pdx::Message& message) {
+  const int user_id = message.GetEffectiveUserId();
+  const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
+
+  // Prevent more than one display manager from registering at a time or
+  // untrusted UIDs from connecting.
+  if (display_manager_ || !trusted) {
+    RemoteMethodError(message, EPERM);
+    return nullptr;
+  }
+
+  display_manager_ =
+      std::make_shared<DisplayManager>(this, message.GetChannelId());
+  return display_manager_;
+}
+
+void DisplayManagerService::OnChannelClose(
+    pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
+  // Unregister the display manager when the channel closes.
+  if (display_manager_ == channel)
+    display_manager_ = nullptr;
+}
+
+pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
+  auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
+
+  switch (message.GetOp()) {
+    case DisplayManagerProtocol::GetSurfaceState::Opcode:
+      DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(
+          *this, &DisplayManagerService::OnGetSurfaceState, message);
+      return {};
+
+    case DisplayManagerProtocol::GetSurfaceQueue::Opcode:
+      DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
+          *this, &DisplayManagerService::OnGetSurfaceQueue, message);
+      return {};
+
+    case DisplayManagerProtocol::SetupNamedBuffer::Opcode:
+      DispatchRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>(
+          *this, &DisplayManagerService::OnSetupNamedBuffer, message);
+      return {};
+
+    default:
+      return Service::DefaultHandleMessage(message);
+  }
+}
+
+pdx::Status<std::vector<display::SurfaceState>>
+DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) {
+  std::vector<display::SurfaceState> items;
+
+  display_service_->ForEachDisplaySurface(
+      SurfaceType::Application,
+      [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
+        items.push_back({surface->surface_id(), surface->process_id(),
+                         surface->user_id(), surface->attributes(),
+                         surface->update_flags(), surface->GetQueueIds()});
+        surface->ClearUpdate();
+      });
+
+  // The fact that we're in the message handler implies that display_manager_ is
+  // not nullptr. No check required, unless this service becomes multi-threaded.
+  display_manager_->SetNotificationsPending(false);
+  return items;
+}
+
+pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue(
+    pdx::Message& /*message*/, int surface_id, int queue_id) {
+  auto surface = display_service_->GetDisplaySurface(surface_id);
+  if (!surface || surface->surface_type() != SurfaceType::Application)
+    return ErrorStatus(EINVAL);
+
+  auto queue =
+      std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue(
+          queue_id);
+  if (!queue)
+    return ErrorStatus(EINVAL);
+
+  auto status = queue->CreateConsumerQueueHandle();
+  ALOGE_IF(
+      !status,
+      "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer "
+      "queue for queue_id=%d: %s",
+      queue->id(), status.GetErrorMessage().c_str());
+
+  return status;
+}
+
+pdx::Status<BorrowedNativeBufferHandle>
+DisplayManagerService::OnSetupNamedBuffer(pdx::Message& message,
+                                          const std::string& name, size_t size,
+                                          uint64_t usage) {
+  const int user_id = message.GetEffectiveUserId();
+  const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
+
+  if (!trusted) {
+    ALOGE(
+        "DisplayService::SetupNamedBuffer: Named buffers may only be created "
+        "by trusted UIDs: user_id=%d",
+        user_id);
+    return ErrorStatus(EPERM);
+  }
+  return display_service_->SetupNamedBuffer(name, size, usage);
+}
+
+void DisplayManagerService::OnDisplaySurfaceChange() {
+  if (display_manager_)
+    display_manager_->SetNotificationsPending(true);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
new file mode 100644
index 0000000..0857eb5
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -0,0 +1,77 @@
+#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
+#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
+
+#include <pdx/service.h>
+#include <pdx/status.h>
+#include <private/dvr/display_protocol.h>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayManagerService;
+
+// The display manager is a client of the display manager service. This class
+// represents the connected client that the display manager service sends
+// notifications to.
+class DisplayManager : public pdx::Channel {
+ public:
+  DisplayManager(DisplayManagerService* service, int channel_id)
+      : service_(service), channel_id_(channel_id) {}
+
+  int channel_id() const { return channel_id_; }
+
+  // Sets or clears the channel event mask to indicate pending events that the
+  // display manager on the other end of the channel should read and handle.
+  // When |pending| is true the POLLIN bit is set in the event mask; when
+  // |pending| is false the POLLIN bit is cleared in the event mask.
+  void SetNotificationsPending(bool pending);
+
+ private:
+  DisplayManager(const DisplayManager&) = delete;
+  void operator=(const DisplayManager&) = delete;
+
+  DisplayManagerService* service_;
+  int channel_id_;
+};
+
+// The display manager service marshalls state and events from the display
+// service to the display manager.
+class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
+ public:
+  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+ private:
+  friend BASE;
+
+  explicit DisplayManagerService(
+      const std::shared_ptr<DisplayService>& display_service);
+
+  pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState(
+      pdx::Message& message);
+  pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message,
+                                                         int surface_id,
+                                                         int queue_id);
+  pdx::Status<BorrowedNativeBufferHandle> OnSetupNamedBuffer(
+      pdx::Message& message, const std::string& name, size_t size,
+      uint64_t usage);
+
+  // Called by the display service to indicate changes to display surfaces that
+  // the display manager should evaluate.
+  void OnDisplaySurfaceChange();
+
+  DisplayManagerService(const DisplayManagerService&) = delete;
+  void operator=(const DisplayManagerService&) = delete;
+
+  std::shared_ptr<DisplayService> display_service_;
+  std::shared_ptr<DisplayManager> display_manager_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
new file mode 100644
index 0000000..47efa76
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -0,0 +1,265 @@
+#include "display_service.h"
+
+#include <unistd.h>
+#include <vector>
+
+#include <dvr/dvr_display_types.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+
+using android::dvr::display::DisplayProtocol;
+using android::pdx::Channel;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Status;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+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();
+}
+
+bool DisplayService::IsInitialized() const {
+  return BASE::IsInitialized() && hardware_composer_.IsInitialized();
+}
+
+std::string DisplayService::DumpState(size_t /*max_length*/) {
+  return hardware_composer_.Dump();
+}
+
+void DisplayService::OnChannelClose(pdx::Message& message,
+                                    const std::shared_ptr<Channel>& channel) {
+  if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
+    surface->OnSetAttributes(message,
+                             {{display::SurfaceAttribute::Visible,
+                               display::SurfaceAttributeValue{false}}});
+    SurfaceUpdated(surface->surface_type(),
+                   display::SurfaceUpdateFlags::VisibilityChanged);
+  }
+}
+
+// First-level dispatch for display service messages. Directly handles messages
+// that are independent of the display surface (metrics, creation) and routes
+// surface-specific messages to the per-instance handlers.
+Status<void> DisplayService::HandleMessage(pdx::Message& message) {
+  ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
+  switch (message.GetOp()) {
+    case DisplayProtocol::GetMetrics::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
+          *this, &DisplayService::OnGetMetrics, message);
+      return {};
+
+    case DisplayProtocol::CreateSurface::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
+          *this, &DisplayService::OnCreateSurface, message);
+      return {};
+
+    case DisplayProtocol::GetNamedBuffer::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::GetNamedBuffer>(
+          *this, &DisplayService::OnGetNamedBuffer, message);
+      return {};
+
+    case DisplayProtocol::IsVrAppRunning::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
+          *this, &DisplayService::IsVrAppRunning, message);
+      return {};
+
+    // Direct the surface specific messages to the surface instance.
+    case DisplayProtocol::SetAttributes::Opcode:
+    case DisplayProtocol::CreateQueue::Opcode:
+    case DisplayProtocol::GetSurfaceInfo::Opcode:
+      return HandleSurfaceMessage(message);
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+Status<display::Metrics> DisplayService::OnGetMetrics(
+    pdx::Message& /*message*/) {
+  return {{static_cast<uint32_t>(GetDisplayMetrics().width),
+           static_cast<uint32_t>(GetDisplayMetrics().height),
+           static_cast<uint32_t>(GetDisplayMetrics().dpi.x),
+           static_cast<uint32_t>(GetDisplayMetrics().dpi.y),
+           static_cast<uint32_t>(
+               hardware_composer_.native_display_metrics().vsync_period_ns),
+           0,
+           0,
+           0,
+           0.0,
+           {},
+           {}}};
+}
+
+// Creates a new DisplaySurface and associates it with this channel. This may
+// only be done once per channel.
+Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
+    pdx::Message& message, const display::SurfaceAttributes& attributes) {
+  // A surface may only be created once per channel.
+  if (message.GetChannel())
+    return ErrorStatus(EINVAL);
+
+  ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
+           message.GetChannelId());
+
+  // Use the channel id as the unique surface id.
+  const int surface_id = message.GetChannelId();
+  const int process_id = message.GetProcessId();
+  const int user_id = message.GetEffectiveUserId();
+
+  ALOGI_IF(TRACE,
+           "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
+           surface_id, process_id);
+
+  auto surface_status =
+      DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
+  if (!surface_status) {
+    ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
+          surface_status.GetErrorMessage().c_str());
+    return ErrorStatus(surface_status.error());
+  }
+
+  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()};
+
+  message.SetChannel(surface_status.take());
+
+  SurfaceUpdated(surface_type, update_flags);
+  return {surface_info};
+}
+
+void DisplayService::SurfaceUpdated(SurfaceType surface_type,
+                                    display::SurfaceUpdateFlags update_flags) {
+  ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
+           update_flags.value());
+  if (update_flags.value() != 0) {
+    if (surface_type == SurfaceType::Application)
+      NotifyDisplayConfigurationUpdate();
+    else
+      UpdateActiveDisplaySurfaces();
+  }
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetNamedBuffer(
+    pdx::Message& /* message */, const std::string& name) {
+  ALOGD_IF(TRACE, "displayService::OnGetNamedBuffer: name=%s", name.c_str());
+  auto named_buffer = named_buffers_.find(name);
+  if (named_buffer != named_buffers_.end())
+    return {BorrowedNativeBufferHandle(*named_buffer->second, 0)};
+  else
+    return pdx::ErrorStatus(EINVAL);
+}
+
+// Calls the message handler for the DisplaySurface associated with this
+// channel.
+Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
+  auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
+  ALOGW_IF(!surface,
+           "DisplayService::HandleSurfaceMessage: surface is nullptr!");
+
+  if (surface)
+    return surface->HandleMessage(message);
+  else
+    return ErrorStatus(EINVAL);
+}
+
+std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
+    int surface_id) const {
+  return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
+}
+
+std::vector<std::shared_ptr<DisplaySurface>>
+DisplayService::GetDisplaySurfaces() const {
+  return GetChannels<DisplaySurface>();
+}
+
+std::vector<std::shared_ptr<DirectDisplaySurface>>
+DisplayService::GetVisibleDisplaySurfaces() const {
+  std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;
+
+  ForEachDisplaySurface(
+      SurfaceType::Direct,
+      [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
+        if (surface->visible()) {
+          visible_surfaces.push_back(
+              std::static_pointer_cast<DirectDisplaySurface>(surface));
+          surface->ClearUpdate();
+        }
+      });
+
+  return visible_surfaces;
+}
+
+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));
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupNamedBuffer(
+    const std::string& name, size_t size, uint64_t usage) {
+  auto named_buffer = named_buffers_.find(name);
+  if (named_buffer == named_buffers_.end()) {
+    auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
+                                                  HAL_PIXEL_FORMAT_BLOB, usage);
+    named_buffer =
+        named_buffers_.insert(std::make_pair(name, std::move(ion_buffer)))
+            .first;
+  }
+
+  return {BorrowedNativeBufferHandle(*named_buffer->second, 0)};
+}
+
+void DisplayService::OnHardwareComposerRefresh() {
+  hardware_composer_.OnHardwareComposerRefresh();
+}
+
+void DisplayService::SetDisplayConfigurationUpdateNotifier(
+    DisplayConfigurationUpdateNotifier update_notifier) {
+  update_notifier_ = update_notifier;
+}
+
+void DisplayService::NotifyDisplayConfigurationUpdate() {
+  if (update_notifier_)
+    update_notifier_();
+}
+
+Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
+  bool visible = false;
+  ForEachDisplaySurface(
+      SurfaceType::Application,
+      [&visible](const std::shared_ptr<DisplaySurface>& surface) {
+        if (surface->visible())
+          visible = true;
+      });
+
+  return {visible};
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
new file mode 100644
index 0000000..bb4eeef
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.h
@@ -0,0 +1,125 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+
+#include <pdx/service.h>
+#include <pdx/status.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/display_protocol.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+#include "epoll_event_dispatcher.h"
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+// DisplayService implements the display service component of VrFlinger.
+class DisplayService : public pdx::ServiceBase<DisplayService> {
+ public:
+  bool IsInitialized() const override;
+  std::string DumpState(size_t max_length) override;
+
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+  std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
+  std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
+  std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces()
+      const;
+
+  // Updates the list of actively displayed surfaces. This must be called after
+  // any change to client/manager attributes that affect visibility or z order.
+  void UpdateActiveDisplaySurfaces();
+
+  pdx::Status<BorrowedNativeBufferHandle> SetupNamedBuffer(
+      const std::string& name, size_t size, uint64_t usage);
+
+  template <class A>
+  void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
+    ForEachChannel([surface_type,
+                    action](const ChannelIterator::value_type& pair) mutable {
+      auto surface = std::static_pointer_cast<DisplaySurface>(pair.second);
+      if (surface->surface_type() == surface_type)
+        action(surface);
+    });
+  }
+
+  using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
+  void SetDisplayConfigurationUpdateNotifier(
+      DisplayConfigurationUpdateNotifier notifier);
+
+  using VSyncCallback = HardwareComposer::VSyncCallback;
+  void SetVSyncCallback(VSyncCallback callback) {
+    hardware_composer_.SetVSyncCallback(callback);
+  }
+
+  HWCDisplayMetrics GetDisplayMetrics() {
+    return hardware_composer_.display_metrics();
+  }
+
+  void GrantDisplayOwnership() { hardware_composer_.Enable(); }
+  void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
+
+  void OnHardwareComposerRefresh();
+
+ private:
+  friend BASE;
+  friend DisplaySurface;
+
+  friend class VrDisplayStateService;
+
+  using RequestDisplayCallback = std::function<void(bool)>;
+
+  DisplayService(android::Hwc2::Composer* hidl,
+                 RequestDisplayCallback request_display_callback);
+
+  pdx::Status<BorrowedNativeBufferHandle> OnGetNamedBuffer(
+      pdx::Message& message, const std::string& name);
+  pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
+  pdx::Status<display::SurfaceInfo> OnCreateSurface(
+      pdx::Message& message, const display::SurfaceAttributes& attributes);
+
+  // Temporary query for current VR status. Will be removed later.
+  pdx::Status<bool> IsVrAppRunning(pdx::Message& message);
+
+  pdx::Status<void> AddEventHandler(int fd, int events,
+                                    EpollEventDispatcher::Handler handler) {
+    return dispatcher_.AddEventHandler(fd, events, handler);
+  }
+  pdx::Status<void> RemoveEventHandler(int fd) {
+    return dispatcher_.RemoveEventHandler(fd);
+  }
+
+  void SurfaceUpdated(SurfaceType surface_type,
+                      display::SurfaceUpdateFlags update_flags);
+
+  // Called by DisplaySurface to signal that a surface property has changed and
+  // the display manager should be notified.
+  void NotifyDisplayConfigurationUpdate();
+
+  pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
+
+  HardwareComposer hardware_composer_;
+  RequestDisplayCallback request_display_callback_;
+  EpollEventDispatcher dispatcher_;
+  DisplayConfigurationUpdateNotifier update_notifier_;
+
+  std::unordered_map<std::string, std::unique_ptr<IonBuffer>> named_buffers_;
+
+  DisplayService(const DisplayService&) = delete;
+  void operator=(const DisplayService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
new file mode 100644
index 0000000..fb2751b
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -0,0 +1,457 @@
+#include "display_surface.h"
+
+#include <private/android_filesystem_config.h>
+#include <utils/Trace.h>
+
+#include <private/dvr/trusted_uids.h>
+
+#include "display_service.h"
+#include "hardware_composer.h"
+
+#define LOCAL_TRACE 1
+
+using android::dvr::display::DisplayProtocol;
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+
+DisplaySurface::DisplaySurface(DisplayService* service,
+                               SurfaceType surface_type, int surface_id,
+                               int process_id, int user_id,
+                               const display::SurfaceAttributes& attributes)
+    : 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() {
+  ALOGD_IF(LOCAL_TRACE,
+           "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
+           surface_id(), process_id());
+}
+
+Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayProtocol::SetAttributes::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::SetAttributes>(
+          *this, &DisplaySurface::OnSetAttributes, message);
+      break;
+
+    case DisplayProtocol::GetSurfaceInfo::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>(
+          *this, &DisplaySurface::OnGetSurfaceInfo, message);
+      break;
+
+    case DisplayProtocol::CreateQueue::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::CreateQueue>(
+          *this, &DisplaySurface::OnCreateQueue, message);
+      break;
+  }
+
+  return {};
+}
+
+Status<void> DisplaySurface::OnSetAttributes(
+    pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) {
+  display::SurfaceUpdateFlags update_flags;
+
+  for (const auto& attribute : attributes) {
+    const auto& key = attribute.first;
+    const auto* variant = &attribute.second;
+    bool invalid_value = false;
+    bool visibility_changed = false;
+
+    // Catch attributes that have significance to the display service.
+    switch (key) {
+      case display::SurfaceAttribute::ZOrder:
+        invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+            variant, [&](const auto& value) {
+              if (z_order_ != value) {
+                visibility_changed = true;
+                z_order_ = value;
+              }
+            });
+        break;
+      case display::SurfaceAttribute::Visible:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [&](const auto& value) {
+              if (visible_ != value) {
+                visibility_changed = true;
+                visible_ = value;
+              }
+            });
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "DisplaySurface::OnClientSetAttributes: Failed to set display "
+          "surface attribute '%d' because of incompatible type: %d",
+          key, variant->index());
+    } else {
+      // Only update the attribute map with valid values.
+      attributes_[attribute.first] = attribute.second;
+
+      // All attribute changes generate a notification, even if the value
+      // doesn't change. Visibility attributes set a flag only if the value
+      // changes.
+      update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged);
+      if (visibility_changed)
+        update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged);
+    }
+  }
+
+  SurfaceUpdated(update_flags);
+  return {};
+}
+
+void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) {
+  ALOGD_IF(TRACE,
+           "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x",
+           surface_id(), update_flags.value());
+
+  update_flags_.Set(update_flags);
+  service()->SurfaceUpdated(surface_type(), update_flags_);
+}
+
+void DisplaySurface::ClearUpdate() {
+  ALOGD_IF(TRACE, "DisplaySurface::ClearUpdate: surface_id=%d", surface_id());
+  update_flags_ = display::SurfaceUpdateFlags::None;
+}
+
+Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo(
+    Message& /*message*/) {
+  ALOGD_IF(
+      TRACE,
+      "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d",
+      surface_id(), visible(), z_order());
+  return {{surface_id(), visible(), z_order()}};
+}
+
+Status<void> DisplaySurface::RegisterQueue(
+    const std::shared_ptr<ConsumerQueue>& consumer_queue) {
+  ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d",
+           surface_id(), consumer_queue->id());
+  // Capture references for the lambda to work around apparent clang bug.
+  // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when
+  // capturing self and consumer_queue by copy in the following case:
+  //    auto self = Self();
+  //    [self, consumer_queue](int events) {
+  //        self->OnQueueEvent(consuemr_queue, events); }
+  //
+  struct State {
+    std::shared_ptr<DisplaySurface> surface;
+    std::shared_ptr<ConsumerQueue> queue;
+  };
+  State state{Self(), consumer_queue};
+
+  return service()->AddEventHandler(
+      consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET,
+      [state](int events) {
+        state.surface->OnQueueEvent(state.queue, events);
+      });
+}
+
+Status<void> DisplaySurface::UnregisterQueue(
+    const std::shared_ptr<ConsumerQueue>& consumer_queue) {
+  ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d",
+           surface_id(), consumer_queue->id());
+  return service()->RemoveEventHandler(consumer_queue->queue_fd());
+}
+
+void DisplaySurface::OnQueueEvent(
+    const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) {
+  ALOGE(
+      "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be "
+      "called!!!");
+}
+
+std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue(
+    int32_t queue_id) {
+  ALOGD_IF(TRACE,
+           "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d",
+           surface_id(), queue_id);
+
+  auto search = consumer_queues_.find(queue_id);
+  if (search != consumer_queues_.end())
+    return search->second;
+  else
+    return nullptr;
+}
+
+std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const {
+  std::vector<int32_t> queue_ids;
+  for (const auto& entry : consumer_queues_)
+    queue_ids.push_back(entry.first);
+  return queue_ids;
+}
+
+Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue(
+    Message& /*message*/, size_t meta_size_bytes) {
+  ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
+  ALOGD_IF(TRACE,
+           "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
+           "meta_size_bytes=%zu",
+           surface_id(), meta_size_bytes);
+
+  std::lock_guard<std::mutex> autolock(lock_);
+  auto producer = ProducerQueue::Create(meta_size_bytes);
+  if (!producer) {
+    ALOGE(
+        "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer "
+        "queue!");
+    return ErrorStatus(ENOMEM);
+  }
+
+  std::shared_ptr<ConsumerQueue> consumer =
+      producer->CreateSilentConsumerQueue();
+  auto status = RegisterQueue(consumer);
+  if (!status) {
+    ALOGE(
+        "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer "
+        "queue: %s",
+        status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+
+  consumer_queues_[consumer->id()] = std::move(consumer);
+
+  SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
+  return std::move(producer->GetChannelHandle());
+}
+
+void ApplicationDisplaySurface::OnQueueEvent(
+    const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
+  ALOGD_IF(TRACE,
+           "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
+           consumer_queue->id(), events);
+
+  // Always give the queue a chance to handle its internal bookkeeping.
+  consumer_queue->HandleQueueEvents();
+
+  // Check for hangup and remove a queue that is no longer needed.
+  std::lock_guard<std::mutex> autolock(lock_);
+  if (consumer_queue->hung_up()) {
+    ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue.");
+    UnregisterQueue(consumer_queue);
+    auto search = consumer_queues_.find(consumer_queue->id());
+    if (search != consumer_queues_.end()) {
+      consumer_queues_.erase(search);
+    } else {
+      ALOGE(
+          "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d",
+          consumer_queue->id());
+    }
+    SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
+  }
+}
+
+Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
+    Message& /*message*/, size_t meta_size_bytes) {
+  ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
+  ALOGD_IF(
+      TRACE,
+      "DirectDisplaySurface::OnCreateQueue: surface_id=%d meta_size_bytes=%zu",
+      surface_id(), meta_size_bytes);
+
+  std::lock_guard<std::mutex> autolock(lock_);
+  if (!direct_queue_) {
+    auto producer = ProducerQueue::Create(meta_size_bytes);
+    if (!producer) {
+      ALOGE(
+          "DirectDisplaySurface::OnCreateQueue: Failed to create producer "
+          "queue!");
+      return ErrorStatus(ENOMEM);
+    }
+
+    direct_queue_ = producer->CreateConsumerQueue();
+    auto status = RegisterQueue(direct_queue_);
+    if (!status) {
+      ALOGE(
+          "DirectDisplaySurface::OnCreateQueue: Failed to register consumer "
+          "queue: %s",
+          status.GetErrorMessage().c_str());
+      return status.error_status();
+    }
+
+    return std::move(producer->GetChannelHandle());
+  } else {
+    return ErrorStatus(EALREADY);
+  }
+}
+
+void DirectDisplaySurface::OnQueueEvent(
+    const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
+  ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
+           consumer_queue->id(), events);
+
+  // Always give the queue a chance to handle its internal bookkeeping.
+  consumer_queue->HandleQueueEvents();
+
+  // Check for hangup and remove a queue that is no longer needed.
+  std::lock_guard<std::mutex> autolock(lock_);
+  if (consumer_queue->hung_up()) {
+    ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue.");
+    UnregisterQueue(consumer_queue);
+    direct_queue_ = nullptr;
+  }
+}
+
+void DirectDisplaySurface::DequeueBuffersLocked() {
+  if (direct_queue_ == nullptr) {
+    ALOGE(
+        "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not "
+        "initialized.");
+    return;
+  }
+
+  while (true) {
+    LocalHandle acquire_fence;
+    size_t slot;
+    auto buffer_status = direct_queue_->Dequeue(0, &slot, &acquire_fence);
+    if (!buffer_status) {
+      ALOGD_IF(
+          TRACE && buffer_status.error() == ETIMEDOUT,
+          "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued.");
+      ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+               "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue "
+               "buffer: %s",
+               buffer_status.GetErrorMessage().c_str());
+      return;
+    }
+    auto buffer_consumer = buffer_status.take();
+
+    if (!visible()) {
+      ATRACE_NAME("DropFrameOnInvisibleSurface");
+      ALOGD_IF(TRACE,
+               "DirectDisplaySurface::DequeueBuffersLocked: Discarding "
+               "buffer_id=%d on invisible surface.",
+               buffer_consumer->id());
+      buffer_consumer->Discard();
+      continue;
+    }
+
+    if (acquired_buffers_.IsFull()) {
+      ALOGE(
+          "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, "
+          "overwriting.");
+      acquired_buffers_.PopBack();
+    }
+
+    acquired_buffers_.Append(
+        AcquiredBuffer(buffer_consumer, std::move(acquire_fence)));
+  }
+}
+
+AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  if (acquired_buffers_.IsEmpty()) {
+    ALOGE(
+        "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer "
+        "when none are posted.");
+    return AcquiredBuffer();
+  }
+  AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
+  acquired_buffers_.PopFront();
+  ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer: %p",
+           buffer.buffer().get());
+  return buffer;
+}
+
+AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer(
+    AcquiredBuffer* skipped_buffer) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  AcquiredBuffer buffer;
+  int frames = 0;
+  // Basic latency stopgap for when the application misses a frame:
+  // If the application recovers on the 2nd or 3rd (etc) frame after
+  // missing, this code will skip frames to catch up by checking if
+  // the next frame is also available.
+  while (!acquired_buffers_.IsEmpty() &&
+         acquired_buffers_.Front().IsAvailable()) {
+    // Capture the skipped buffer into the result parameter.
+    // Note that this API only supports skipping one buffer per vsync.
+    if (frames > 0 && skipped_buffer)
+      *skipped_buffer = std::move(buffer);
+    ++frames;
+    buffer = std::move(acquired_buffers_.Front());
+    acquired_buffers_.PopFront();
+    if (frames == 2)
+      break;
+  }
+  ALOGD_IF(TRACE,
+           "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer: %p",
+           buffer.buffer().get());
+  return buffer;
+}
+
+bool DirectDisplaySurface::IsBufferAvailable() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  return !acquired_buffers_.IsEmpty() &&
+         acquired_buffers_.Front().IsAvailable();
+}
+
+bool DirectDisplaySurface::IsBufferPosted() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  return !acquired_buffers_.IsEmpty();
+}
+
+Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create(
+    DisplayService* service, int surface_id, int process_id, int user_id,
+    const display::SurfaceAttributes& attributes) {
+  bool direct = false;
+  auto search = attributes.find(display::SurfaceAttribute::Direct);
+  if (search != attributes.end()) {
+    if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second,
+                                                     &direct)) {
+      ALOGE(
+          "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!");
+      return ErrorStatus(EINVAL);
+    }
+  }
+
+  ALOGD_IF(TRACE,
+           "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d "
+           "direct=%d",
+           surface_id, process_id, user_id, direct);
+
+  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)}};
+    } else {
+      ALOGE(
+          "DisplaySurface::Create: Direct surfaces may only be created by "
+          "trusted UIDs: user_id=%d",
+          user_id);
+      return ErrorStatus(EPERM);
+    }
+  } else {
+    return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
+        service, surface_id, process_id, user_id, attributes)}};
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
new file mode 100644
index 0000000..c456b10
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -0,0 +1,185 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+
+#include <pdx/file_handle.h>
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+enum class SurfaceType {
+  Direct,
+  Application,
+};
+
+class DisplaySurface : public pdx::Channel {
+ public:
+  static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
+      DisplayService* service, int surface_id, int process_id, int user_id,
+      const display::SurfaceAttributes& attributes);
+
+  ~DisplaySurface() override;
+
+  DisplayService* service() const { return service_; }
+  SurfaceType surface_type() const { return surface_type_; }
+  int surface_id() const { return surface_id_; }
+  int process_id() const { return process_id_; }
+  int user_id() const { return user_id_; }
+
+  bool visible() const { return visible_; }
+  int z_order() const { return z_order_; }
+
+  const display::SurfaceAttributes& attributes() const { return attributes_; }
+  display::SurfaceUpdateFlags update_flags() const { return update_flags_; }
+
+  virtual std::vector<int32_t> GetQueueIds() const { return {}; }
+
+  bool IsUpdatePending() const {
+    return update_flags_.value() != display::SurfaceUpdateFlags::None;
+  }
+
+ protected:
+  DisplaySurface(DisplayService* service, SurfaceType surface_type,
+                 int surface_id, int process_id, int user_id,
+                 const display::SurfaceAttributes& attributes);
+
+  // Utility to retrieve a shared pointer to this channel as the desired derived
+  // type.
+  template <
+      typename T = DisplaySurface,
+      typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
+  std::shared_ptr<T> Self() {
+    return std::static_pointer_cast<T>(shared_from_this());
+  }
+
+  virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+      pdx::Message& message, size_t meta_size_bytes) = 0;
+
+  // Registers a consumer queue with the event dispatcher in DisplayService. The
+  // OnQueueEvent callback below is called to handle queue events.
+  pdx::Status<void> RegisterQueue(
+      const std::shared_ptr<ConsumerQueue>& consumer_queue);
+  pdx::Status<void> UnregisterQueue(
+      const std::shared_ptr<ConsumerQueue>& consumer_queue);
+
+  // Called by the event dispatcher in DisplayService when a registered queue
+  // event triggers. Executes on the event dispatcher thread.
+  virtual void OnQueueEvent(
+      const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);
+
+  void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
+  void ClearUpdate();
+
+  // Synchronizes access to mutable state below between message dispatch thread
+  // and frame post thread.
+  mutable std::mutex lock_;
+
+ private:
+  friend class DisplayService;
+  friend class DisplayManagerService;
+
+  // Dispatches display surface messages to the appropriate handlers. This
+  // handler runs on the VrFlinger message dispatch thread.
+  pdx::Status<void> HandleMessage(pdx::Message& message);
+
+  pdx::Status<void> OnSetAttributes(
+      pdx::Message& message, const display::SurfaceAttributes& attributes);
+  pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);
+
+  DisplayService* service_;
+  SurfaceType surface_type_;
+  int surface_id_;
+  int process_id_;
+  int user_id_;
+
+  display::SurfaceAttributes attributes_;
+  display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;
+
+  // Subset of attributes that may be interpreted by the display service.
+  bool visible_ = false;
+  int z_order_ = 0;
+
+  DisplaySurface(const DisplaySurface&) = delete;
+  void operator=(const DisplaySurface&) = delete;
+};
+
+class ApplicationDisplaySurface : public DisplaySurface {
+ public:
+  ApplicationDisplaySurface(DisplayService* service, int surface_id,
+                            int process_id, int user_id,
+                            const display::SurfaceAttributes& attributes)
+      : DisplaySurface(service, SurfaceType::Application, surface_id,
+                       process_id, user_id, attributes) {}
+
+  std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
+  std::vector<int32_t> GetQueueIds() const override;
+
+ private:
+  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+      pdx::Message& message, size_t meta_size_bytes) override;
+  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
+                    int events) override;
+
+  std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
+};
+
+class DirectDisplaySurface : public DisplaySurface {
+ public:
+  DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
+                       int user_id,
+                       const display::SurfaceAttributes& attributes)
+      : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
+                       user_id, attributes),
+        acquired_buffers_(kMaxPostedBuffers) {}
+  bool IsBufferAvailable();
+  bool IsBufferPosted();
+  AcquiredBuffer AcquireCurrentBuffer();
+
+  // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
+  // skipped, it will be stored in skipped_buffer if non null.
+  AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
+
+ private:
+  pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+      pdx::Message& message, size_t meta_size_bytes) override;
+  void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
+                    int events) override;
+
+  // The capacity of the pending buffer queue. Should be enough to hold all the
+  // buffers of this DisplaySurface, although in practice only 1 or 2 frames
+  // will be pending at a time.
+  static constexpr int kSurfaceBufferMaxCount = 4;
+  static constexpr int kSurfaceViewMaxCount = 4;
+  static constexpr int kMaxPostedBuffers =
+      kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
+
+  // Returns whether a frame is available without locking the mutex.
+  bool IsFrameAvailableNoLock() const;
+
+  // Dequeue all available buffers from the consumer queue.
+  void DequeueBuffersLocked();
+
+  // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
+  // posted and pending.
+  RingBuffer<AcquiredBuffer> acquired_buffers_;
+
+  std::shared_ptr<ConsumerQueue> direct_queue_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
new file mode 100644
index 0000000..06b69bb
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -0,0 +1,142 @@
+#include "epoll_event_dispatcher.h"
+
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/prctl.h>
+
+#include <dvr/performance_client_api.h>
+
+namespace android {
+namespace dvr {
+
+EpollEventDispatcher::EpollEventDispatcher() {
+  epoll_fd_.Reset(epoll_create(64));
+  if (!epoll_fd_) {
+    ALOGE("Failed to create epoll fd: %s", strerror(errno));
+    return;
+  }
+
+  event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  if (!event_fd_) {
+    ALOGE("Failed to create event for epolling: %s", strerror(errno));
+    return;
+  }
+
+  // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
+  // when eventfd_write occurs. Use "this" as a unique sentinal value to
+  // identify events from the event fd.
+  epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
+    ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
+    return;
+  }
+
+  thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
+}
+
+EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
+
+void EpollEventDispatcher::Stop() {
+  exit_thread_.store(true);
+  eventfd_write(event_fd_.Get(), 1);
+}
+
+pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
+                                                        Handler handler) {
+  std::lock_guard<std::mutex> lock(lock_);
+
+  epoll_event event;
+  event.events = event_mask;
+  event.data.ptr = &(handlers_[fd] = handler);
+
+  ALOGD_IF(
+      TRACE,
+      "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
+      fd, event_mask, event.data.ptr);
+
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
+    const int error = errno;
+    ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
+    return pdx::ErrorStatus(error);
+  } else {
+    return {};
+  }
+}
+
+pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
+  ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
+  std::lock_guard<std::mutex> lock(lock_);
+
+  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
+    const int error = errno;
+    ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
+    return pdx::ErrorStatus(error);
+  }
+
+  // If the fd was valid above, add it to the list of ids to remove.
+  removed_handlers_.push_back(fd);
+
+  // Wake up the event thread to clean up.
+  eventfd_write(event_fd_.Get(), 1);
+
+  return {};
+}
+
+void EpollEventDispatcher::EventThread() {
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
+
+  const int error = dvrSetSchedulerClass(0, "graphics");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
+      strerror(-error));
+
+  const size_t kMaxNumEvents = 128;
+  epoll_event events[kMaxNumEvents];
+
+  while (!exit_thread_.load()) {
+    const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
+    if (num_events < 0 && errno != EINTR)
+      break;
+
+    ALOGD_IF(TRACE, "EpollEventDispatcher::EventThread: num_events=%d",
+             num_events);
+
+    for (int i = 0; i < num_events; i++) {
+      ALOGD_IF(
+          TRACE,
+          "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
+          i, events[i].data.ptr, events[i].events);
+
+      if (events[i].data.ptr == this) {
+        // Clear pending event on event_fd_. Serialize the read with respect to
+        // writes from other threads.
+        std::lock_guard<std::mutex> lock(lock_);
+        eventfd_t value;
+        eventfd_read(event_fd_.Get(), &value);
+      } else {
+        auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
+        if (handler)
+          (*handler)(events[i].events);
+      }
+    }
+
+    // Remove any handlers that have been posted for removal. This is done here
+    // instead of in RemoveEventHandler() to prevent races between the dispatch
+    // thread and the code requesting the removal. Handlers are guaranteed to
+    // stay alive between exiting epoll_wait() and the dispatch loop above.
+    std::lock_guard<std::mutex> lock(lock_);
+    for (auto handler_fd : removed_handlers_) {
+      ALOGD_IF(TRACE,
+               "EpollEventDispatcher::EventThread: removing handler: fd=%d",
+               handler_fd);
+      handlers_.erase(handler_fd);
+    }
+    removed_handlers_.clear();
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
new file mode 100644
index 0000000..eb687f4
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+
+#include <sys/epoll.h>
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace dvr {
+
+class EpollEventDispatcher {
+ public:
+  // Function type for event handlers. The handler receives a bitmask of the
+  // epoll events that occurred on the file descriptor associated with the
+  // handler.
+  using Handler = std::function<void(int)>;
+
+  EpollEventDispatcher();
+  ~EpollEventDispatcher();
+
+  // |handler| is called on the internal dispatch thread when |fd| is signaled
+  // by events in |event_mask|.
+  pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler);
+  pdx::Status<void> RemoveEventHandler(int fd);
+
+  void Stop();
+
+ private:
+  void EventThread();
+
+  std::thread thread_;
+  std::atomic<bool> exit_thread_{false};
+
+  // Protects handlers_ and removed_handlers_ and serializes operations on
+  // epoll_fd_ and event_fd_.
+  std::mutex lock_;
+
+  // Maintains a map of fds to event handlers. This is primarily to keep any
+  // references alive that may be bound in the std::function instances. It is
+  // not used at dispatch time to avoid performance problems with different
+  // versions of std::unordered_map.
+  std::unordered_map<int, Handler> handlers_;
+
+  // List of fds to be removed from the map. The actual removal is performed
+  // by the event dispatch thread to avoid races.
+  std::vector<int> removed_handlers_;
+
+  pdx::LocalHandle epoll_fd_;
+  pdx::LocalHandle event_fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
new file mode 100644
index 0000000..34474d9
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -0,0 +1,1134 @@
+#include "hardware_composer.h"
+
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <poll.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 <time.h>
+#include <unistd.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <map>
+
+#include <dvr/dvr_display_types.h>
+#include <dvr/performance_client_api.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/ion_buffer.h>
+#include <private/dvr/pose_client_internal.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::rpc::EmptyVariant;
+using android::pdx::rpc::IfAnyOf;
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// If the number of pending fences goes over this count at the point when we
+// are about to submit a new frame to HWC, we will drop the frame. This should
+// be a signal that the display driver has begun queuing frames. Note that with
+// smart displays (with RAM), the fence is signaled earlier than the next vsync,
+// at the point when the DMA to the display completes. Currently we use a smart
+// display and the EDS timing coincides with zero pending fences, so this is 0.
+constexpr int kAllowedPendingFenceCount = 0;
+
+// Offset before vsync to submit frames to hardware composer.
+constexpr int64_t kFramePostOffsetNs = 4000000;  // 4ms
+
+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 kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
+
+// Get time offset from a vsync to when the pose for that vsync should be
+// predicted out to. For example, if scanout gets halfway through the frame
+// at the halfway point between vsyncs, then this could be half the period.
+// With global shutter displays, this should be changed to the offset to when
+// illumination begins. Low persistence adds a frame of latency, so we predict
+// to the center of the next frame.
+inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
+  return (vsync_period_ns * 150) / 100;
+}
+
+// Attempts to set the scheduler class and partiton for the current thread.
+// Returns true on success or false on failure.
+bool SetThreadPolicy(const std::string& scheduler_class,
+                     const std::string& partition) {
+  int error = dvrSetSchedulerClass(0, scheduler_class.c_str());
+  if (error < 0) {
+    ALOGE(
+        "SetThreadPolicy: Failed to set scheduler class \"%s\" for "
+        "thread_id=%d: %s",
+        scheduler_class.c_str(), gettid(), strerror(-error));
+    return false;
+  }
+  error = dvrSetCpuPartition(0, partition.c_str());
+  if (error < 0) {
+    ALOGE(
+        "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: "
+        "%s",
+        partition.c_str(), gettid(), strerror(-error));
+    return false;
+  }
+  return true;
+}
+
+}  // 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) {}
+
+HardwareComposer::~HardwareComposer(void) {
+  UpdatePostThreadState(PostThreadState::Quit, true);
+  if (post_thread_.joinable())
+    post_thread_.join();
+}
+
+bool HardwareComposer::Initialize() {
+  if (initialized_) {
+    ALOGE("HardwareComposer::Initialize: already initialized.");
+    return false;
+  }
+
+  HWC::Error error = HWC::Error::None;
+
+  Hwc2::Config config;
+  error = hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+
+  if (error != HWC::Error::None) {
+    ALOGE("HardwareComposer: Failed to get current display config : %d",
+          config);
+    return false;
+  }
+
+  error =
+      GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_);
+
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer: Failed to get display attributes for current "
+        "configuration : %d",
+        error.value);
+    return false;
+  }
+
+  ALOGI(
+      "HardwareComposer: primary display attributes: width=%d height=%d "
+      "vsync_period_ns=%d DPI=%dx%d",
+      native_display_metrics_.width, native_display_metrics_.height,
+      native_display_metrics_.vsync_period_ns, native_display_metrics_.dpi.x,
+      native_display_metrics_.dpi.y);
+
+  // Set the display metrics but never use rotation to avoid the long latency of
+  // rotation processing in hwc.
+  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_);
+
+  post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  LOG_ALWAYS_FATAL_IF(
+      !post_thread_event_fd_,
+      "HardwareComposer: Failed to create interrupt event fd : %s",
+      strerror(errno));
+
+  post_thread_ = std::thread(&HardwareComposer::PostThread, this);
+
+  initialized_ = true;
+
+  return initialized_;
+}
+
+void HardwareComposer::Enable() {
+  UpdatePostThreadState(PostThreadState::Suspended, false);
+}
+
+void HardwareComposer::Disable() {
+  UpdatePostThreadState(PostThreadState::Suspended, true);
+}
+
+// Update the post thread quiescent state based on idle and suspended inputs.
+void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
+                                             bool suspend) {
+  std::unique_lock<std::mutex> lock(post_thread_mutex_);
+
+  // Update the votes in the state variable before evaluating the effective
+  // quiescent state. Any bits set in post_thread_state_ indicate that the post
+  // thread should be suspended.
+  if (suspend) {
+    post_thread_state_ |= state;
+  } else {
+    post_thread_state_ &= ~state;
+  }
+
+  const bool quit = post_thread_state_ & PostThreadState::Quit;
+  const bool effective_suspend = post_thread_state_ != PostThreadState::Active;
+  if (quit) {
+    post_thread_quiescent_ = true;
+    eventfd_write(post_thread_event_fd_.Get(), 1);
+    post_thread_wait_.notify_one();
+  } else if (effective_suspend && !post_thread_quiescent_) {
+    post_thread_quiescent_ = true;
+    eventfd_write(post_thread_event_fd_.Get(), 1);
+  } else if (!effective_suspend && post_thread_quiescent_) {
+    post_thread_quiescent_ = false;
+    eventfd_t value;
+    eventfd_read(post_thread_event_fd_.Get(), &value);
+    post_thread_wait_.notify_one();
+  }
+
+  // Wait until the post thread is in the requested state.
+  post_thread_ready_.wait(lock, [this, effective_suspend] {
+    return effective_suspend != post_thread_resumed_;
+  });
+}
+
+void HardwareComposer::OnPostThreadResumed() {
+  hwc2_hidl_->resetCommands();
+
+  // Connect to pose service.
+  pose_client_ = dvrPoseCreate();
+  ALOGE_IF(!pose_client_, "HardwareComposer: Failed to create pose client");
+
+  // 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.
+  // TODO(steventhomas): When we change the backlight setting, will surface
+  // flinger (or something else) set it back to its original value once we give
+  // control of the display back to surface flinger?
+  SetBacklightBrightness(255);
+
+  // Trigger target-specific performance mode change.
+  property_set(kDvrPerformanceProperty, "performance");
+}
+
+void HardwareComposer::OnPostThreadPaused() {
+  retire_fence_fds_.clear();
+  display_surfaces_.clear();
+
+  for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
+    layers_[i].Reset();
+  }
+  active_layer_count_ = 0;
+
+  if (pose_client_) {
+    dvrPoseDestroy(pose_client_);
+    pose_client_ = nullptr;
+  }
+
+  EnableVsync(false);
+
+  hwc2_hidl_->resetCommands();
+
+  // Trigger target-specific performance mode change.
+  property_set(kDvrPerformanceProperty, "idle");
+}
+
+HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
+  uint32_t num_types;
+  uint32_t num_requests;
+  HWC::Error error =
+      hwc2_hidl_->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);
+  }
+
+  return error;
+}
+
+int32_t HardwareComposer::EnableVsync(bool enabled) {
+  return (int32_t)hwc2_hidl_->setVsyncEnabled(
+      HWC_DISPLAY_PRIMARY,
+      (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
+                                             : HWC2_VSYNC_DISABLE));
+}
+
+HWC::Error HardwareComposer::Present(hwc2_display_t display) {
+  int32_t present_fence;
+  HWC::Error error = hwc2_hidl_->presentDisplay(display, &present_fence);
+
+  // According to the documentation, this fence is signaled at the time of
+  // vsync/DMA for physical displays.
+  if (error == HWC::Error::None) {
+    ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
+    retire_fence_fds_.emplace_back(present_fence);
+  } else {
+    ATRACE_INT("HardwareComposer: PresentResult", error);
+  }
+
+  return error;
+}
+
+HWC::Error HardwareComposer::GetDisplayAttribute(hwc2_display_t display,
+                                                 hwc2_config_t config,
+                                                 hwc2_attribute_t attribute,
+                                                 int32_t* out_value) const {
+  return hwc2_hidl_->getDisplayAttribute(
+      display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
+}
+
+HWC::Error HardwareComposer::GetDisplayMetrics(
+    hwc2_display_t display, hwc2_config_t config,
+    HWCDisplayMetrics* out_metrics) const {
+  HWC::Error error;
+
+  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH,
+                              &out_metrics->width);
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer::GetDisplayMetrics: Failed to get display width: %s",
+        error.to_string().c_str());
+    return error;
+  }
+
+  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT,
+                              &out_metrics->height);
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
+        error.to_string().c_str());
+    return error;
+  }
+
+  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+                              &out_metrics->vsync_period_ns);
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
+        error.to_string().c_str());
+    return error;
+  }
+
+  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X,
+                              &out_metrics->dpi.x);
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer::GetDisplayMetrics: Failed to get display DPI X: %s",
+        error.to_string().c_str());
+    return error;
+  }
+
+  error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y,
+                              &out_metrics->dpi.y);
+  if (error != HWC::Error::None) {
+    ALOGE(
+        "HardwareComposer::GetDisplayMetrics: Failed to get display DPI Y: %s",
+        error.to_string().c_str());
+    return error;
+  }
+
+  return HWC::Error::None;
+}
+
+std::string HardwareComposer::Dump() { return hwc2_hidl_->dumpDebugInfo(); }
+
+void HardwareComposer::PostLayers() {
+  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();
+  }
+
+  HWC::Error error = Validate(HWC_DISPLAY_PRIMARY);
+  if (error != HWC::Error::None) {
+    ALOGE("HardwareComposer::PostLayers: Validate failed: %s",
+          error.to_string().c_str());
+    return;
+  }
+
+  // Now that we have taken in a frame from the application, we have a chance
+  // to drop the frame before passing the frame along to HWC.
+  // If the display driver has become backed up, we detect it here and then
+  // react by skipping this frame to catch up latency.
+  while (!retire_fence_fds_.empty() &&
+         (!retire_fence_fds_.front() ||
+          sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
+    // There are only 2 fences in here, no performance problem to shift the
+    // array of ints.
+    retire_fence_fds_.erase(retire_fence_fds_.begin());
+  }
+
+  const bool is_frame_pending = IsFramePendingInDriver();
+  const bool is_fence_pending =
+      retire_fence_fds_.size() > kAllowedPendingFenceCount;
+
+  if (is_fence_pending || is_frame_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();
+    }
+    return;
+  } else {
+    // Make the transition more obvious in systrace when the frame skip happens
+    // above.
+    ATRACE_INT("frame_skip_count", 0);
+  }
+
+#if TRACE
+  for (size_t i = 0; i < active_layer_count_; i++)
+    ALOGI("HardwareComposer::PostLayers: layer=%zu composition=%s", i,
+          layers_[i].GetCompositionType().to_string().c_str());
+#endif
+
+  error = Present(HWC_DISPLAY_PRIMARY);
+  if (error != HWC::Error::None) {
+    ALOGE("HardwareComposer::PostLayers: Present failed: %s",
+          error.to_string().c_str());
+    return;
+  }
+
+  std::vector<Hwc2::Layer> out_layers;
+  std::vector<int> out_fences;
+  error = hwc2_hidl_->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.
+  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]);
+      }
+    }
+  }
+}
+
+void HardwareComposer::SetDisplaySurfaces(
+    std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) {
+  ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
+        surfaces.size());
+  const bool display_idle = surfaces.size() == 0;
+  {
+    std::unique_lock<std::mutex> lock(post_thread_mutex_);
+    pending_surfaces_ = std::move(surfaces);
+  }
+
+  // Set idle state based on whether there are any surfaces to handle.
+  UpdatePostThreadState(PostThreadState::Idle, display_idle);
+
+  // XXX: TEMPORARY
+  // Request control of the display based on whether there are any surfaces to
+  // handle. This callback sets the post thread active state once the transition
+  // is complete in SurfaceFlinger.
+  // TODO(eieio): Unify the control signal used to move SurfaceFlinger into VR
+  // mode. Currently this is hooked up to persistent VR mode, but perhaps this
+  // makes more sense to control it from VrCore, which could in turn base its
+  // decision on persistent VR mode.
+  if (request_display_callback_)
+    request_display_callback_(!display_idle);
+}
+
+int HardwareComposer::PostThreadPollInterruptible(
+    const pdx::LocalHandle& event_fd, int requested_events) {
+  pollfd pfd[2] = {
+      {
+          .fd = event_fd.Get(),
+          .events = static_cast<short>(requested_events),
+          .revents = 0,
+      },
+      {
+          .fd = post_thread_event_fd_.Get(),
+          .events = POLLPRI | POLLIN,
+          .revents = 0,
+      },
+  };
+  int ret, error;
+  do {
+    ret = poll(pfd, 2, -1);
+    error = errno;
+    ALOGW_IF(ret < 0,
+             "HardwareComposer::PostThreadPollInterruptible: Error during "
+             "poll(): %s (%d)",
+             strerror(error), error);
+  } while (ret < 0 && error == EINTR);
+
+  if (ret < 0) {
+    return -error;
+  } else if (pfd[0].revents != 0) {
+    return 0;
+  } else if (pfd[1].revents != 0) {
+    ALOGI("VrHwcPost thread interrupted");
+    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);
+}
+
+// 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;
+    }
+  }
+}
+
+int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
+  const int timer_fd = vsync_sleep_timer_fd_.Get();
+  const itimerspec wakeup_itimerspec = {
+      .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+      .it_value = NsToTimespec(wakeup_timestamp),
+  };
+  int ret =
+      timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
+  int error = errno;
+  if (ret < 0) {
+    ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
+          strerror(error));
+    return -error;
+  }
+
+  return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN);
+}
+
+void HardwareComposer::PostThread() {
+  // NOLINTNEXTLINE(runtime/int)
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0);
+
+  // Set the scheduler to SCHED_FIFO with high priority. If this fails here
+  // there may have been a startup timing issue between this thread and
+  // performanced. Try again later when this thread becomes active.
+  bool thread_policy_setup =
+      SetThreadPolicy("graphics:high", "/system/performance");
+
+#if ENABLE_BACKLIGHT_BRIGHTNESS
+  // TODO(hendrikw): This isn't required at the moment. It's possible that there
+  //                 is another method to access this when needed.
+  // Open the backlight brightness control sysfs node.
+  backlight_brightness_fd_ = LocalHandle(kBacklightBrightnessSysFile, O_RDWR);
+  ALOGW_IF(!backlight_brightness_fd_,
+           "HardwareComposer: Failed to open backlight brightness control: %s",
+           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(
+      !vsync_sleep_timer_fd_,
+      "HardwareComposer: Failed to create vsync sleep timerfd: %s",
+      strerror(errno));
+
+  const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
+  const int64_t photon_offset_ns = GetPosePredictionTimeOffset(ns_per_frame);
+
+  // TODO(jbates) Query vblank time from device, when such an API is available.
+  // This value (6.3%) was measured on A00 in low persistence mode.
+  int64_t vblank_ns = ns_per_frame * 63 / 1000;
+  int64_t right_eye_photon_offset_ns = (ns_per_frame - vblank_ns) / 2;
+
+  // Check property for overriding right eye offset value.
+  right_eye_photon_offset_ns =
+      property_get_int64(kRightEyeOffsetProperty, right_eye_photon_offset_ns);
+
+  bool was_running = false;
+
+  while (1) {
+    ATRACE_NAME("HardwareComposer::PostThread");
+
+    while (post_thread_quiescent_) {
+      std::unique_lock<std::mutex> lock(post_thread_mutex_);
+      ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
+
+      // Tear down resources.
+      OnPostThreadPaused();
+
+      was_running = false;
+      post_thread_resumed_ = false;
+      post_thread_ready_.notify_all();
+
+      if (post_thread_state_ & PostThreadState::Quit) {
+        ALOGI("HardwareComposer::PostThread: Quitting.");
+        return;
+      }
+
+      post_thread_wait_.wait(lock, [this] { return !post_thread_quiescent_; });
+
+      post_thread_resumed_ = true;
+      post_thread_ready_.notify_all();
+
+      ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
+    }
+
+    if (!was_running) {
+      // Setup resources.
+      OnPostThreadResumed();
+      was_running = true;
+
+      // Try to setup the scheduler policy if it failed during startup. Only
+      // attempt to do this on transitions from inactive to active to avoid
+      // spamming the system with RPCs and log messages.
+      if (!thread_policy_setup) {
+        thread_policy_setup =
+            SetThreadPolicy("graphics:high", "/system/performance");
+      }
+    }
+
+    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());
+
+      const int error = WaitForVSync(&vsync_timestamp);
+      ALOGE_IF(
+          error < 0,
+          "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)
+        continue;
+    }
+
+    ++vsync_count_;
+
+    if (pose_client_) {
+      // Signal the pose service with vsync info.
+      // Display timestamp is in the middle of scanout.
+      privateDvrPoseNotifyVsync(pose_client_, vsync_count_,
+                                vsync_timestamp + photon_offset_ns,
+                                ns_per_frame, right_eye_photon_offset_ns);
+    }
+
+    const bool layer_config_changed = UpdateLayerConfig();
+
+    // Signal all of the vsync clients. Because absolute time is used for the
+    // wakeup time below, this can take a little time if necessary.
+    if (vsync_callback_)
+      vsync_callback_(HWC_DISPLAY_PRIMARY, vsync_timestamp,
+                      /*frame_time_estimate*/ 0, vsync_count_);
+
+    {
+      // Sleep until shortly before vsync.
+      ATRACE_NAME("sleep");
+
+      const int64_t display_time_est_ns = vsync_timestamp + ns_per_frame;
+      const int64_t now_ns = GetSystemClockNs();
+      const int64_t sleep_time_ns =
+          display_time_est_ns - now_ns - kFramePostOffsetNs;
+      const int64_t wakeup_time_ns = display_time_est_ns - kFramePostOffsetNs;
+
+      ATRACE_INT64("sleep_time_ns", sleep_time_ns);
+      if (sleep_time_ns > 0) {
+        int error = SleepUntil(wakeup_time_ns);
+        ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s",
+                 strerror(-error));
+        if (error == kPostThreadInterrupted) {
+          if (layer_config_changed) {
+            // If the layer config changed we need to validateDisplay() even if
+            // we're going to drop the frame, to flush the Composer object's
+            // internal command buffer and apply our layer changes.
+            Validate(HWC_DISPLAY_PRIMARY);
+          }
+          continue;
+        }
+      }
+    }
+
+    PostLayers();
+  }
+}
+
+// Checks for changes in the surface stack and updates the layer config to
+// accomodate the new stack.
+bool HardwareComposer::UpdateLayerConfig() {
+  std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
+  {
+    std::unique_lock<std::mutex> lock(post_thread_mutex_);
+    if (pending_surfaces_.empty())
+      return false;
+
+    surfaces = std::move(pending_surfaces_);
+  }
+
+  ATRACE_NAME("UpdateLayerConfig_HwLayers");
+
+  display_surfaces_.clear();
+
+  Layer* target_layer;
+  size_t layer_index;
+  for (layer_index = 0;
+       layer_index < std::min(surfaces.size(), kMaxHardwareLayers);
+       layer_index++) {
+    // 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]);
+  }
+
+  // Clear unused layers.
+  for (size_t i = layer_index; i < kMaxHardwareLayers; i++)
+    layers_[i].Reset();
+
+  active_layer_count_ = layer_index;
+  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());
+
+  return true;
+}
+
+void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
+  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;
+    const int length = snprintf(text.data(), text.size(), "%d", brightness);
+    write(backlight_brightness_fd_.Get(), text.data(), length);
+  }
+}
+
+void Layer::InitializeGlobals(Hwc2::Composer* hwc2_hidl,
+                              const HWCDisplayMetrics* metrics) {
+  hwc2_hidl_ = hwc2_hidl;
+  display_metrics_ = metrics;
+}
+
+void Layer::Reset() {
+  if (hwc2_hidl_ != nullptr && hardware_composer_layer_) {
+    hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+    hardware_composer_layer_ = 0;
+  }
+
+  z_order_ = 0;
+  blending_ = HWC::BlendMode::None;
+  transform_ = HWC::Transform::None;
+  composition_type_ = HWC::Composition::Invalid;
+  target_composition_type_ = composition_type_;
+  source_ = EmptyVariant{};
+  acquire_fence_.Close();
+  surface_rect_functions_applied_ = false;
+}
+
+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};
+  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};
+  CommonLayerSetup();
+}
+
+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; }
+
+IonBuffer* Layer::GetBuffer() {
+  struct Visitor {
+    IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); }
+    IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); }
+    IonBuffer* operator()(EmptyVariant) { return nullptr; }
+  };
+  return source_.Visit(Visitor{});
+}
+
+void Layer::UpdateLayerSettings() {
+  if (!IsLayerSetup()) {
+    ALOGE(
+        "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update "
+        "unused Layer!");
+    return;
+  }
+
+  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());
+
+  // TODO(eieio): Use surface attributes or some other mechanism to control
+  // the layer display frame.
+  error = hwc2_hidl_->setLayerDisplayFrame(
+      display, hardware_composer_layer_,
+      {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(
+      display, hardware_composer_layer_,
+      {{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);
+  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());
+  UpdateLayerSettings();
+}
+
+void Layer::Prepare() {
+  int right, bottom;
+  sp<GraphicBuffer> handle;
+
+  // 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();
+  });
+
+  // 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(
+          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);
+    } else {
+      // The composition type is already set. Nothing else to do until a
+      // buffer arrives.
+    }
+  } else {
+    if (composition_type_ != target_composition_type_) {
+      composition_type_ = target_composition_type_;
+      hwc2_hidl_->setLayerCompositionType(
+          HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+          composition_type_.cast<Hwc2::IComposerClient::Composition>());
+    }
+
+    HWC::Error error{HWC::Error::None};
+    error = hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
+                                       hardware_composer_layer_, 0, handle,
+                                       acquire_fence_.Get());
+
+    ALOGE_IF(error != HWC::Error::None,
+             "Layer::Prepare: Error setting layer buffer: %s",
+             error.to_string().c_str());
+
+    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});
+
+      ALOGE_IF(error != HWC::Error::None,
+               "Layer::Prepare: Error setting layer source crop: %s",
+               error.to_string().c_str());
+
+      surface_rect_functions_applied_ = true;
+    }
+  }
+}
+
+void Layer::Finish(int release_fence_fd) {
+  IfAnyOf<SourceSurface, SourceBuffer>::Call(
+      &source_, [release_fence_fd](auto& source) {
+        source.Finish(LocalHandle(release_fence_fd));
+      });
+}
+
+void Layer::Drop() { acquire_fence_.Close(); }
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
new file mode 100644
index 0000000..8ba72ab
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -0,0 +1,457 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+
+#include <ui/GraphicBuffer.h>
+#include "DisplayHardware/ComposerHal.h"
+#include "hwc_types.h"
+
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <array>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+
+// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
+#ifndef HWC_TRANSFORM_NONE
+#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
+#endif
+
+namespace android {
+namespace dvr {
+
+// Basic display metrics for physical displays. Dimensions and densities are
+// relative to the physical display orientation, which may be different from the
+// logical display orientation exposed to applications.
+struct HWCDisplayMetrics {
+  int width;
+  int height;
+  struct {
+    int x;
+    int y;
+  } dpi;
+  int vsync_period_ns;
+};
+
+// Layer represents the connection between a hardware composer layer and the
+// 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();
+
+  // 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
+  // every frame.
+  //
+  // |blending| receives HWC_BLENDING_* values.
+  // |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).
+  // |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);
+
+  // 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
+  // changing the buffer each frame is on the caller.
+  //
+  // |blending| receives HWC_BLENDING_* values.
+  // |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);
+
+  // Layers that use a direct IonBuffer should call this each frame to update
+  // which buffer will be used for the next PostLayers.
+  void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer);
+
+  // Sets up the hardware composer layer for the next frame. When the layer is
+  // associated with a display surface, this method automatically ACQUIRES a new
+  // buffer if one is available.
+  void Prepare();
+
+  // After calling prepare, if this frame is to be dropped instead of passing
+  // along to the HWC, call Drop to close the contained fence(s).
+  void Drop();
+
+  // Performs fence bookkeeping after the frame has been posted to hardware
+  // composer.
+  void Finish(int release_fence_fd);
+
+  // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
+  void SetBlending(HWC::BlendMode blending);
+
+  // Sets the z-order of this layer
+  void SetZOrder(size_t z_order);
+
+  // Gets the current IonBuffer associated with this layer. Ownership of the
+  // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
+  // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
+  // Layer::Reset(). YOU HAVE BEEN WARNED.
+  IonBuffer* GetBuffer();
+
+  HWC::Composition GetCompositionType() const { return composition_type_; }
+  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(
+        &source_, [&surface_id](const SourceSurface& surface_source) {
+          surface_id = surface_source.surface->surface_id();
+        });
+    return surface_id;
+  }
+
+ private:
+  void CommonLayerSetup();
+
+  static Hwc2::Composer* hwc2_hidl_;
+  static const HWCDisplayMetrics* display_metrics_;
+
+  // The hardware composer layer and metrics to use during the prepare cycle.
+  hwc2_layer_t hardware_composer_layer_ = 0;
+
+  // Layer properties used to setup the hardware composer layer during the
+  // Prepare phase.
+  size_t z_order_ = 0;
+  HWC::BlendMode blending_ = HWC::BlendMode::None;
+  HWC::Transform transform_ = HWC::Transform::None;
+  HWC::Composition composition_type_ = HWC::Composition::Invalid;
+  HWC::Composition target_composition_type_ = HWC::Composition::Device;
+
+  // State when the layer is connected to a surface. Provides the same interface
+  // as SourceBuffer to simplify internal use by Layer.
+  struct SourceSurface {
+    std::shared_ptr<DirectDisplaySurface> surface;
+    AcquiredBuffer acquired_buffer;
+    pdx::LocalHandle release_fence;
+
+    SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface)
+        : surface(surface) {}
+
+    // Attempts to acquire a new buffer from the surface and return a tuple with
+    // width, height, buffer handle, and fence. If a new buffer is not available
+    // 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() {
+      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());
+      } else {
+        return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+      }
+    }
+
+    void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); }
+
+    // Gets a pointer to the current acquired buffer or returns nullptr if there
+    // isn't one.
+    IonBuffer* GetBuffer() {
+      if (acquired_buffer.IsAvailable())
+        return acquired_buffer.buffer()->buffer();
+      else
+        return nullptr;
+    }
+
+    // Returns the surface id of the surface.
+    int GetSurfaceId() { return surface->surface_id(); }
+  };
+
+  // State when the layer is connected to a buffer. Provides the same interface
+  // as SourceSurface to simplify internal use by Layer.
+  struct SourceBuffer {
+    std::shared_ptr<IonBuffer> buffer;
+
+    std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+      if (buffer)
+        return std::make_tuple(buffer->width(), buffer->height(),
+                               buffer->buffer(), pdx::LocalHandle{});
+      else
+        return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+    }
+
+    void Finish(pdx::LocalHandle /*fence*/) {}
+
+    IonBuffer* GetBuffer() { return buffer.get(); }
+
+    int GetSurfaceId() const { return -1; }
+  };
+
+  // The underlying hardware composer layer is supplied buffers either from a
+  // surface buffer train or from a buffer directly.
+  pdx::rpc::Variant<SourceSurface, SourceBuffer> source_;
+
+  pdx::LocalHandle acquire_fence_;
+  bool surface_rect_functions_applied_ = false;
+
+  Layer(const Layer&) = delete;
+  void operator=(const Layer&) = delete;
+};
+
+// HardwareComposer encapsulates the hardware composer HAL, exposing a
+// simplified API to post buffers to the display.
+//
+// HardwareComposer is accessed by both the vr flinger dispatcher thread and the
+// surface flinger main thread, in addition to internally running a separate
+// thread for compositing/EDS and posting layers to the HAL. When changing how
+// variables are used or adding new state think carefully about which threads
+// will access the state and whether it needs to be synchronized.
+class HardwareComposer {
+ public:
+  // Type for vsync callback.
+  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 IsInitialized() const { return initialized_; }
+
+  // Start the post thread if there's work to do (i.e. visible layers). This
+  // should only be called from surface flinger's main thread.
+  void Enable();
+  // Pause the post thread, blocking until the post thread has signaled that
+  // it's paused. This should only be called from surface flinger's main thread.
+  void Disable();
+
+  // 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);
+
+  // Metrics of the logical display, which is always landscape.
+  int DisplayWidth() const { return display_metrics_.width; }
+  int DisplayHeight() const { return display_metrics_.height; }
+  HWCDisplayMetrics display_metrics() const { return display_metrics_; }
+
+  // Metrics of the native display, which depends on the specific hardware
+  // implementation of the display.
+  HWCDisplayMetrics native_display_metrics() const {
+    return native_display_metrics_;
+  }
+
+  // Sets the display surfaces to compose the hardware layer stack.
+  void SetDisplaySurfaces(
+      std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
+
+  void OnHardwareComposerRefresh();
+
+ private:
+  int32_t EnableVsync(bool enabled);
+
+  class ComposerCallback : public Hwc2::IComposerCallback {
+   public:
+    ComposerCallback() {}
+
+    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();
+    }
+
+    hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override {
+      return hardware::Void();
+    }
+
+    hardware::Return<void> onVsync(Hwc2::Display /*display*/,
+                                   int64_t /*timestamp*/) override {
+      return hardware::Void();
+    }
+  };
+
+  HWC::Error Validate(hwc2_display_t display);
+  HWC::Error Present(hwc2_display_t display);
+
+  void SetBacklightBrightness(int brightness);
+
+  void PostLayers();
+  void PostThread();
+
+  // The post thread has two controlling states:
+  // 1. Idle: no work to do (no visible surfaces).
+  // 2. Suspended: explicitly halted (system is not in VR mode).
+  // When either #1 or #2 is true then the post thread is quiescent, otherwise
+  // it is active.
+  using PostThreadStateType = uint32_t;
+  struct PostThreadState {
+    enum : PostThreadStateType {
+      Active = 0,
+      Idle = (1 << 0),
+      Suspended = (1 << 1),
+      Quit = (1 << 2),
+    };
+  };
+
+  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.
+  int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
+                                  int requested_events);
+
+  // 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.
+  int ReadWaitPPState();
+  int BlockUntilVSync();
+  int ReadVSyncTimestamp(int64_t* timestamp);
+  int WaitForVSync(int64_t* timestamp);
+  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();
+
+  // Called on the post thread when the post thread is resumed.
+  void OnPostThreadResumed();
+  // Called on the post thread when the post thread is paused or quits.
+  void OnPostThreadPaused();
+
+  bool initialized_;
+
+  // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own
+  // this pointer.
+  Hwc2::Composer* hwc2_hidl_;
+  RequestDisplayCallback request_display_callback_;
+  sp<ComposerCallback> callbacks_;
+
+  // Display metrics of the physical display.
+  HWCDisplayMetrics native_display_metrics_;
+  // Display metrics of the logical display, adjusted so that orientation is
+  // landscape.
+  HWCDisplayMetrics display_metrics_;
+  // Transform required to get from native to logical display orientation.
+  HWC::Transform display_transform_ = HWC::Transform::None;
+
+  // Pending surface list. Set by the display service when DirectSurfaces are
+  // added, removed, or change visibility. Written by the message dispatch
+  // 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;
+
+  // Handler to hook vsync events outside of this class.
+  VSyncCallback vsync_callback_;
+
+  // The layer posting thread. This thread wakes up a short time before vsync to
+  // hand buffers to hardware composer.
+  std::thread post_thread_;
+
+  // Post thread state machine and synchronization primitives.
+  PostThreadStateType post_thread_state_{PostThreadState::Idle};
+  std::atomic<bool> post_thread_quiescent_{true};
+  bool post_thread_resumed_{false};
+  pdx::LocalHandle post_thread_event_fd_;
+  std::mutex post_thread_mutex_;
+  std::condition_variable post_thread_wait_;
+  std::condition_variable post_thread_ready_;
+
+  // 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;
+
+  // Vsync count since display on.
+  uint32_t vsync_count_ = 0;
+
+  // Counter tracking the number of skipped frames.
+  int frame_skip_count_ = 0;
+
+  // Fd array for tracking retire fences that are returned by hwc. This allows
+  // us to detect when the display driver begins queuing frames.
+  std::vector<pdx::LocalHandle> retire_fence_fds_;
+
+  // Pose client for frame count notifications. Pose client predicts poses
+  // out to display frame boundaries, so we need to tell it about vsyncs.
+  DvrPose* pose_client_ = nullptr;
+
+  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;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h
new file mode 100644
index 0000000..cbf636c
--- /dev/null
+++ b/libs/vr/libvrflinger/hwc_types.h
@@ -0,0 +1,300 @@
+#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H
+#define ANDROID_LIBVRFLINGER_HWCTYPES_H
+
+// General HWC type support. Hardware composer type support is a bit of a mess
+// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the
+// use of enum classes, which make analogous types between versions much
+// harder to deal with in a uniform way.
+//
+// These utilities help address some of these pains by providing a type-safe,
+// flexible interface to translate between different type spaces.
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include <string>
+#include <type_traits>
+
+namespace HWC {
+
+// Value types derived from HWC HAL types. Some of these are stand-alone,
+// while others are also wrapped in translator classes below.
+using ColorMode = int32_t;  // android_color_mode_t;
+using Config = hwc2_config_t;
+using ColorTransform =
+    std::underlying_type<android_color_transform_t>::type;          // int32_t;
+using Dataspace = std::underlying_type<android_dataspace_t>::type;  // int32_t;
+using DisplayId = hwc2_display_t;
+using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type;
+using Hdr = std::underlying_type<android_hdr_t>::type;  // int32_t;
+using Layer = hwc2_layer_t;
+using PixelFormat =
+    std::underlying_type<android_pixel_format_t>::type;  // int32_t;
+
+// Type traits and casting utilities.
+
+// SFINAE utility to evaluate type expressions.
+template <typename...>
+using TestTypeExpression = void;
+
+// Traits type to determine the underlying type of an enum, integer,
+// or wrapper class.
+template <typename T, typename = typename std::is_enum<T>::type,
+          typename = typename std::is_integral<T>::type, typename = void>
+struct UnderlyingType {
+  using Type = T;
+};
+// Partial specialization that matches enum types. Captures the underlying type
+// of the enum in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::true_type, std::false_type> {
+  using Type = typename std::underlying_type<T>::type;
+};
+// Partial specialization that matches integral types. Captures the type of the
+// integer in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::false_type, std::true_type> {
+  using Type = T;
+};
+// Partial specialization that matches the wrapper types below. Captures
+// wrapper member type ValueType in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::false_type, std::false_type,
+                      TestTypeExpression<typename T::ValueType>> {
+  using Type = typename T::ValueType;
+};
+
+// Enable if T is an enum with underlying type U.
+template <typename T, typename U, typename ReturnType = void>
+using EnableIfMatchingEnum = typename std::enable_if<
+    std::is_enum<T>::value &&
+        std::is_same<U, typename UnderlyingType<T>::Type>::value,
+    ReturnType>::type;
+
+// Enable if T and U are the same size/alignment and have the same underlying
+// type. Handles enum, integral, and wrapper classes below.
+template <typename T, typename U, typename Return = void>
+using EnableIfSafeCast = typename std::enable_if<
+    sizeof(T) == sizeof(U) && alignof(T) == alignof(U) &&
+        std::is_same<typename UnderlyingType<T>::Type,
+                     typename UnderlyingType<U>::Type>::value,
+    Return>::type;
+
+// Safely cast between std::vectors of matching enum/integer/wraper types.
+// Normally this is not possible with pendantic compiler type checks. However,
+// given the same size, alignment, and underlying type this is safe due to
+// allocator requirements and array-like element access guarantees.
+template <typename T, typename U>
+EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) {
+  return reinterpret_cast<std::vector<T>*>(in);
+}
+
+// Translator classes that wrap specific HWC types to make translating
+// between different types (especially enum class) in code cleaner.
+
+// Base type for the enum wrappers below. This type provides type definitions
+// and implicit conversion logic common to each wrapper type.
+template <typename EnumType>
+struct Wrapper {
+  // Alias type of this instantiantion of Wrapper. Useful for inheriting
+  // constructors in subclasses via "using Base::Base;" statements.
+  using Base = Wrapper<EnumType>;
+
+  // The enum type wrapped by this instantiation of Wrapper.
+  using BaseType = EnumType;
+
+  // The underlying type of the base enum type.
+  using ValueType = typename UnderlyingType<BaseType>::Type;
+
+  // A default constructor is not defined here. Subclasses should define one
+  // as appropriate to define the correct inital value for the enum type.
+
+  // Default copy constructor.
+  Wrapper(const Wrapper&) = default;
+
+  // Implicit conversion from ValueType.
+  Wrapper(ValueType value) : value(value) {}
+
+  // Implicit conversion from BaseType.
+  Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {}
+
+  // Implicit conversion from an enum type of the same underlying type.
+  template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
+  Wrapper(const T& value) : value(static_cast<ValueType>(value)) {}
+
+  // Implicit conversion to BaseType.
+  operator BaseType() const { return static_cast<BaseType>(value); }
+
+  // Implicit conversion to ValueType.
+  operator ValueType() const { return value; }
+
+  template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
+  T cast() const {
+    return static_cast<T>(value);
+  }
+
+  // Converts to string using HWC2 stringification of BaseType.
+  std::string to_string() const {
+    return HWC2::to_string(static_cast<BaseType>(value));
+  }
+
+  bool operator!=(const Wrapper& other) const { return value != other.value; }
+  bool operator!=(ValueType other_value) const { return value != other_value; }
+  bool operator!=(BaseType other_value) const {
+    return static_cast<BaseType>(value) != other_value;
+  }
+  bool operator==(const Wrapper& other) const { return value == other.value; }
+  bool operator==(ValueType other_value) const { return value == other_value; }
+  bool operator==(BaseType other_value) const {
+    return static_cast<BaseType>(value) == other_value;
+  }
+
+  ValueType value;
+};
+
+struct Attribute final : public Wrapper<HWC2::Attribute> {
+  enum : ValueType {
+    Invalid = HWC2_ATTRIBUTE_INVALID,
+    Width = HWC2_ATTRIBUTE_WIDTH,
+    Height = HWC2_ATTRIBUTE_HEIGHT,
+    VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD,
+    DpiX = HWC2_ATTRIBUTE_DPI_X,
+    DpiY = HWC2_ATTRIBUTE_DPI_Y,
+  };
+
+  Attribute() : Base(Invalid) {}
+  using Base::Base;
+};
+
+struct BlendMode final : public Wrapper<HWC2::BlendMode> {
+  enum : ValueType {
+    Invalid = HWC2_BLEND_MODE_INVALID,
+    None = HWC2_BLEND_MODE_NONE,
+    Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED,
+    Coverage = HWC2_BLEND_MODE_COVERAGE,
+  };
+
+  BlendMode() : Base(Invalid) {}
+  using Base::Base;
+};
+
+struct Composition final : public Wrapper<HWC2::Composition> {
+  enum : ValueType {
+    Invalid = HWC2_COMPOSITION_INVALID,
+    Client = HWC2_COMPOSITION_CLIENT,
+    Device = HWC2_COMPOSITION_DEVICE,
+    SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
+    Cursor = HWC2_COMPOSITION_CURSOR,
+    Sideband = HWC2_COMPOSITION_SIDEBAND,
+  };
+
+  Composition() : Base(Invalid) {}
+  using Base::Base;
+};
+
+struct DisplayType final : public Wrapper<HWC2::DisplayType> {
+  enum : ValueType {
+    Invalid = HWC2_DISPLAY_TYPE_INVALID,
+    Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
+    Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
+  };
+
+  DisplayType() : Base(Invalid) {}
+  using Base::Base;
+};
+
+struct Error final : public Wrapper<HWC2::Error> {
+  enum : ValueType {
+    None = HWC2_ERROR_NONE,
+    BadConfig = HWC2_ERROR_BAD_CONFIG,
+    BadDisplay = HWC2_ERROR_BAD_DISPLAY,
+    BadLayer = HWC2_ERROR_BAD_LAYER,
+    BadParameter = HWC2_ERROR_BAD_PARAMETER,
+    HasChanges = HWC2_ERROR_HAS_CHANGES,
+    NoResources = HWC2_ERROR_NO_RESOURCES,
+    NotValidated = HWC2_ERROR_NOT_VALIDATED,
+    Unsupported = HWC2_ERROR_UNSUPPORTED,
+  };
+
+  Error() : Base(None) {}
+  using Base::Base;
+};
+
+struct LayerRequest final : public Wrapper<HWC2::LayerRequest> {
+  enum : ValueType {
+    ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET,
+  };
+
+  LayerRequest() : Base(0) {}
+  using Base::Base;
+};
+
+struct PowerMode final : public Wrapper<HWC2::PowerMode> {
+  enum : ValueType {
+    Off = HWC2_POWER_MODE_OFF,
+    DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND,
+    Doze = HWC2_POWER_MODE_DOZE,
+    On = HWC2_POWER_MODE_ON,
+  };
+
+  PowerMode() : Base(Off) {}
+  using Base::Base;
+};
+
+struct Transform final : public Wrapper<HWC2::Transform> {
+  enum : ValueType {
+    None = 0,
+    FlipH = HWC_TRANSFORM_FLIP_H,
+    FlipV = HWC_TRANSFORM_FLIP_V,
+    Rotate90 = HWC_TRANSFORM_ROT_90,
+    Rotate180 = HWC_TRANSFORM_ROT_180,
+    Rotate270 = HWC_TRANSFORM_ROT_270,
+    FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90,
+    FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90,
+  };
+
+  Transform() : Base(None) {}
+  using Base::Base;
+};
+
+struct Vsync final : public Wrapper<HWC2::Vsync> {
+  enum : ValueType {
+    Invalid = HWC2_VSYNC_INVALID,
+    Enable = HWC2_VSYNC_ENABLE,
+    Disable = HWC2_VSYNC_DISABLE,
+  };
+
+  Vsync() : Base(Invalid) {}
+  using Base::Base;
+};
+
+// Utility color type.
+struct Color final {
+  Color(const Color&) = default;
+  Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
+  Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {}
+
+  operator hwc_color_t() const { return {r, g, b, a}; }
+
+  uint8_t r __attribute__((aligned(1)));
+  uint8_t g __attribute__((aligned(1)));
+  uint8_t b __attribute__((aligned(1)));
+  uint8_t a __attribute__((aligned(1)));
+};
+
+// Utility rectangle type.
+struct Rect final {
+  // TODO(eieio): Implicit conversion to/from Android rect types.
+
+  int32_t left __attribute__((aligned(4)));
+  int32_t top __attribute__((aligned(4)));
+  int32_t right __attribute__((aligned(4)));
+  int32_t bottom __attribute__((aligned(4)));
+};
+
+}  // namespace HWC
+
+#endif  // ANDROID_LIBVRFLINGER_HWCTYPES_H
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
new file mode 100644
index 0000000..145852e
--- /dev/null
+++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_DVR_VR_FLINGER_H_
+#define ANDROID_DVR_VR_FLINGER_H_
+
+#include <thread>
+#include <memory>
+
+#include <pdx/default_transport/service_dispatcher.h>
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+
+namespace Hwc2 {
+class Composer;
+}  // namespace Hwc2
+
+namespace dvr {
+
+class DisplayService;
+
+class VrFlinger {
+ public:
+  using RequestDisplayCallback = std::function<void(bool)>;
+  static std::unique_ptr<VrFlinger> Create(
+      Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback);
+  ~VrFlinger();
+
+  // These functions are all called on surface flinger's main thread.
+  void OnBootFinished();
+  void GrantDisplayOwnership();
+  void SeizeDisplayOwnership();
+
+  // Called on a binder thread.
+  void OnHardwareComposerRefresh();
+
+ private:
+  VrFlinger();
+  bool Init(Hwc2::Composer* hidl,
+            RequestDisplayCallback request_display_callback);
+
+  // Needs to be a separate class for binder's ref counting
+  class PersistentVrStateCallback : public BnPersistentVrStateCallbacks {
+   public:
+    PersistentVrStateCallback(RequestDisplayCallback request_display_callback)
+        : request_display_callback_(request_display_callback) {}
+    void onPersistentVrStateChanged(bool enabled) override;
+   private:
+    RequestDisplayCallback request_display_callback_;
+  };
+
+  std::thread dispatcher_thread_;
+  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_;
+  std::shared_ptr<android::dvr::DisplayService> display_service_;
+  sp<PersistentVrStateCallback> persistent_vr_state_callback_;
+  RequestDisplayCallback request_display_callback_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
new file mode 100644
index 0000000..b2dc1d8
--- /dev/null
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -0,0 +1,151 @@
+#include <dvr/vr_flinger.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <memory>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+#include <private/dvr/display_client.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include <functional>
+
+#include "DisplayHardware/ComposerHal.h"
+#include "display_manager_service.h"
+#include "display_service.h"
+#include "vsync_service.h"
+
+namespace android {
+namespace dvr {
+
+std::unique_ptr<VrFlinger> VrFlinger::Create(
+    Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) {
+  std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger);
+  if (vr_flinger->Init(hidl, request_display_callback))
+    return vr_flinger;
+  else
+    return nullptr;
+}
+
+VrFlinger::VrFlinger() {}
+
+VrFlinger::~VrFlinger() {
+  if (persistent_vr_state_callback_.get()) {
+    sp<IVrManager> vr_manager = interface_cast<IVrManager>(
+        defaultServiceManager()->checkService(String16("vrmanager")));
+    if (vr_manager.get()) {
+      vr_manager->unregisterPersistentVrStateListener(
+          persistent_vr_state_callback_);
+    }
+  }
+
+  if (dispatcher_)
+    dispatcher_->SetCanceled(true);
+  if (dispatcher_thread_.joinable())
+    dispatcher_thread_.join();
+}
+
+bool VrFlinger::Init(Hwc2::Composer* hidl,
+                     RequestDisplayCallback request_display_callback) {
+  if (!hidl || !request_display_callback)
+    return false;
+
+  std::shared_ptr<android::pdx::Service> service;
+
+  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);
+
+  android::ProcessState::self()->startThreadPool();
+
+  request_display_callback_ = request_display_callback;
+
+  dispatcher_ = android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
+
+  display_service_ =
+      android::dvr::DisplayService::Create(hidl, request_display_callback);
+  CHECK_ERROR(!display_service_, error, "Failed to create display service.");
+  dispatcher_->AddService(display_service_);
+
+  service = android::dvr::DisplayManagerService::Create(display_service_);
+  CHECK_ERROR(!service, error, "Failed to create display manager service.");
+  dispatcher_->AddService(service);
+
+  service = android::dvr::VSyncService::Create();
+  CHECK_ERROR(!service, error, "Failed to create vsync service.");
+  dispatcher_->AddService(service);
+
+  display_service_->SetVSyncCallback(
+      std::bind(&android::dvr::VSyncService::VSyncEvent,
+                std::static_pointer_cast<android::dvr::VSyncService>(service),
+                std::placeholders::_1, std::placeholders::_2,
+                std::placeholders::_3, std::placeholders::_4));
+
+  dispatcher_thread_ = std::thread([this]() {
+    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
+    ALOGI("Entering message loop.");
+
+    int ret = dispatcher_->EnterDispatchLoop();
+    if (ret < 0) {
+      ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
+    }
+  });
+
+  return true;
+
+error:
+  return false;
+}
+
+void VrFlinger::OnBootFinished() {
+  sp<IVrManager> vr_manager = interface_cast<IVrManager>(
+      defaultServiceManager()->checkService(String16("vrmanager")));
+  if (vr_manager.get()) {
+    persistent_vr_state_callback_ =
+        new PersistentVrStateCallback(request_display_callback_);
+    vr_manager->registerPersistentVrStateListener(
+        persistent_vr_state_callback_);
+  } else {
+    ALOGE("Unable to register vr flinger for persistent vr mode changes");
+  }
+}
+
+void VrFlinger::GrantDisplayOwnership() {
+  display_service_->GrantDisplayOwnership();
+}
+
+void VrFlinger::SeizeDisplayOwnership() {
+  display_service_->SeizeDisplayOwnership();
+}
+
+void VrFlinger::OnHardwareComposerRefresh() {
+  display_service_->OnHardwareComposerRefresh();
+}
+
+void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
+    bool enabled) {
+  ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
+  // TODO(eieio): Determine the correct signal to request display control.
+  // Persistent VR mode is not enough.
+  // request_display_callback_(enabled);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
new file mode 100644
index 0000000..2a83933
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -0,0 +1,213 @@
+#include "vsync_service.h"
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <dvr/dvr_display_types.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_protocol.h>
+
+using android::dvr::display::VSyncProtocol;
+using android::dvr::display::VSyncSchedInfo;
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VSyncService::VSyncService()
+    : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)),
+      last_vsync_(0),
+      current_vsync_(0),
+      compositor_time_ns_(0),
+      current_vsync_count_(0) {}
+
+VSyncService::~VSyncService() {}
+
+void VSyncService::VSyncEvent(int display, int64_t timestamp_ns,
+                              int64_t compositor_time_ns,
+                              uint32_t vsync_count) {
+  ATRACE_NAME("VSyncService::VSyncEvent");
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  if (display == HWC_DISPLAY_PRIMARY) {
+    last_vsync_ = current_vsync_;
+    current_vsync_ = timestamp_ns;
+    compositor_time_ns_ = compositor_time_ns;
+    current_vsync_count_ = vsync_count;
+
+    NotifyWaiters();
+    UpdateClients();
+  }
+}
+
+std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
+  const MessageInfo& info = message.GetInfo();
+
+  auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
+  AddClient(client);
+
+  return client;
+}
+
+void VSyncService::OnChannelClose(pdx::Message& /*message*/,
+                                  const std::shared_ptr<Channel>& channel) {
+  auto client = std::static_pointer_cast<VSyncChannel>(channel);
+  if (!client) {
+    ALOGW("WARNING: VSyncChannel was NULL!!!\n");
+    return;
+  }
+
+  RemoveClient(client);
+}
+
+void VSyncService::AddWaiter(pdx::Message& message) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
+  waiters_.push_back(std::move(waiter));
+}
+
+void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  clients_.push_back(client);
+}
+
+void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  clients_.remove(client);
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::NotifyWaiters() {
+  ATRACE_NAME("VSyncService::NotifyWaiters");
+  auto first = waiters_.begin();
+  auto last = waiters_.end();
+
+  while (first != last) {
+    (*first)->Notify(current_vsync_);
+    waiters_.erase(first++);
+  }
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::UpdateClients() {
+  ATRACE_NAME("VSyncService::UpdateClients");
+  auto first = clients_.begin();
+  auto last = clients_.end();
+
+  while (first != last) {
+    (*first)->Signal();
+    first++;
+  }
+}
+
+pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case VSyncProtocol::Wait::Opcode:
+      AddWaiter(message);
+      return {};
+
+    case VSyncProtocol::GetLastTimestamp::Opcode:
+      DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>(
+          *this, &VSyncService::OnGetLastTimestamp, message);
+      return {};
+
+    case VSyncProtocol::GetSchedInfo::Opcode:
+      DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>(
+          *this, &VSyncService::OnGetSchedInfo, message);
+      return {};
+
+    case VSyncProtocol::Acknowledge::Opcode:
+      DispatchRemoteMethod<VSyncProtocol::Acknowledge>(
+          *this, &VSyncService::OnAcknowledge, message);
+      return {};
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  // Getting the timestamp has the side effect of ACKing.
+  client->Ack();
+  return {current_vsync_};
+}
+
+pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo(
+    pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  // Getting the timestamp has the side effect of ACKing.
+  client->Ack();
+
+  uint32_t next_vsync_count = current_vsync_count_ + 1;
+  int64_t current_time = GetSystemClockNs();
+  int64_t vsync_period_ns = 0;
+  int64_t next_warp;
+  if (current_vsync_ == 0 || last_vsync_ == 0) {
+    // Handle startup when current_vsync_ or last_vsync_ are 0.
+    // Normally should not happen because vsync_service is running before
+    // applications, but in case it does a sane time prevents applications
+    // from malfunctioning.
+    vsync_period_ns = 20000000;
+    next_warp = current_time;
+  } else {
+    // TODO(jbates) When we have an accurate reading of the true vsync
+    // period, use that instead of this estimated value.
+    vsync_period_ns = current_vsync_ - last_vsync_;
+    // Clamp the period, because when there are no surfaces the last_vsync_
+    // value will get stale. Note this is temporary and goes away as soon
+    // as we have an accurate vsync period reported by the system.
+    vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
+    next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
+    // If the request missed the present window, move up to the next vsync.
+    if (current_time > next_warp) {
+      next_warp += vsync_period_ns;
+      ++next_vsync_count;
+    }
+  }
+
+  return {{vsync_period_ns, next_warp, next_vsync_count}};
+}
+
+pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+  client->Ack();
+  return {};
+}
+
+void VSyncWaiter::Notify(int64_t timestamp) {
+  timestamp_ = timestamp;
+  DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait,
+                                            message_);
+}
+
+pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) {
+  return {timestamp_};
+}
+
+void VSyncChannel::Ack() {
+  ALOGD_IF(TRACE, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
+  service_.ModifyChannelEvents(cid_, POLLPRI, 0);
+}
+
+void VSyncChannel::Signal() {
+  ALOGD_IF(TRACE, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
+  service_.ModifyChannelEvents(cid_, 0, POLLPRI);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
new file mode 100644
index 0000000..215948e
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.h
@@ -0,0 +1,106 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+
+#include <pdx/service.h>
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
+// It is used to enqueue the Message to reply to when the next vsync event
+// occurs.
+class VSyncWaiter {
+ public:
+  explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
+
+  void Notify(int64_t timestamp);
+
+ private:
+  pdx::Status<int64_t> OnWait(pdx::Message& message);
+
+  pdx::Message message_;
+  int64_t timestamp_ = 0;
+
+  VSyncWaiter(const VSyncWaiter&) = delete;
+  void operator=(const VSyncWaiter&) = delete;
+};
+
+// VSyncChannel manages the service-side per-client context for each client
+// using the service.
+class VSyncChannel : public pdx::Channel {
+ public:
+  VSyncChannel(pdx::Service& service, int pid, int cid)
+      : service_(service), pid_(pid), cid_(cid) {}
+
+  void Ack();
+  void Signal();
+
+ private:
+  pdx::Service& service_;
+  pid_t pid_;
+  int cid_;
+
+  VSyncChannel(const VSyncChannel&) = delete;
+  void operator=(const VSyncChannel&) = delete;
+};
+
+// VSyncService implements the displayd vsync service over ServiceFS.
+class VSyncService : public pdx::ServiceBase<VSyncService> {
+ public:
+  ~VSyncService() override;
+
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+
+  // Called by the hardware composer HAL, or similar, whenever a vsync event
+  // occurs. |compositor_time_ns| is the number of ns before the next vsync when
+  // the compositor will preempt the GPU to do EDS and lens warp.
+  void VSyncEvent(int display, int64_t timestamp_ns, int64_t compositor_time_ns,
+                  uint32_t vsync_count);
+
+ private:
+  friend BASE;
+
+  VSyncService();
+
+  pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message);
+  pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message);
+  pdx::Status<void> OnAcknowledge(pdx::Message& message);
+
+  void NotifierThreadFunction();
+
+  void AddWaiter(pdx::Message& message);
+  void NotifyWaiters();
+  void UpdateClients();
+
+  void AddClient(const std::shared_ptr<VSyncChannel>& client);
+  void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
+
+  int64_t last_vsync_;
+  int64_t current_vsync_;
+  int64_t compositor_time_ns_;
+  uint32_t current_vsync_count_;
+
+  std::mutex mutex_;
+
+  std::list<std::unique_ptr<VSyncWaiter>> waiters_;
+  std::list<std::shared_ptr<VSyncChannel>> clients_;
+
+  VSyncService(const VSyncService&) = delete;
+  void operator=(VSyncService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
new file mode 100644
index 0000000..abad78b
--- /dev/null
+++ b/libs/vr/libvrsensor/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+sourceFiles = [
+    "pose_client.cpp",
+    "sensor_client.cpp",
+    "latency_model.cpp",
+]
+
+includeFiles = [
+    "include",
+]
+
+staticLibraries = [
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "libhardware",
+    "liblog",
+    "libutils",
+    "libui",
+]
+
+cc_library {
+    srcs: sourceFiles,
+    export_include_dirs: includeFiles,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "libvrsensor",
+}
+
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libvrsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000..ed75f84
--- /dev/null
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -0,0 +1,205 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_H_
+#define ANDROID_DVR_POSE_CLIENT_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrPose DvrPose;
+
+// Represents the current state provided by the pose service, containing a
+// rotation and translation.
+typedef struct __attribute__((packed, aligned(8))) DvrPoseState {
+  // A quaternion representing the rotation of the HMD in Start Space.
+  struct __attribute__((packed)) {
+    float x, y, z, w;
+  } head_from_start_rotation;
+  // The position of the HMD in Start Space.
+  struct __attribute__((packed)) {
+    float x, y, z;
+  } head_from_start_translation;
+  // Time in nanoseconds for the current pose.
+  uint64_t timestamp_ns;
+  // The rotational velocity of the HMD.
+  struct __attribute__((packed)) {
+    float x, y, z;
+  } sensor_from_start_rotation_velocity;
+} DvrPoseState;
+
+enum {
+  DVR_POSE_FLAG_VALID = (1UL << 0),       // This pose is valid.
+  DVR_POSE_FLAG_HEAD = (1UL << 1),        // This pose is the head.
+  DVR_POSE_FLAG_CONTROLLER = (1UL << 2),  // This pose is a controller.
+};
+
+// 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.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync {
+  // Left eye head-from-start orientation quaternion x,y,z,w.
+  float32x4_t orientation;
+  // Left eye head-from-start translation x,y,z,pad in meters.
+  float32x4_t translation;
+  // Right eye head-from-start orientation quaternion x,y,z,w.
+  float32x4_t right_orientation;
+  // Right eye head-from-start translation x,y,z,pad in meters.
+  float32x4_t right_translation;
+  // Start-space angular velocity x,y,z,pad in radians per second.
+  float32x4_t angular_velocity;
+  // Start-space positional velocity x,y,z,pad in meters per second.
+  float32x4_t velocity;
+  // Timestamp of when this pose is predicted for, typically halfway through
+  // scanout.
+  int64_t timestamp_ns;
+  // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose.
+  //
+  // If DVR_POSE_FLAG_VALID is not set, the pose is indeterminate.
+  uint64_t flags;
+  // Reserved padding to 128 bytes.
+  uint8_t pad[16];
+} DvrPoseAsync;
+
+// Returned by the async pose ring buffer access API.
+typedef struct DvrPoseRingBufferInfo {
+  // Read-only pointer to the pose ring buffer. The current pose is in this
+  // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
+  // frame's forecasted pose is at element
+  // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
+  // predicted for when 50% of the corresponding frame's pixel data is visible
+  // to the user.
+  // The last value returned by dvrPresent is the count for the next frame,
+  // which is the earliest that the application could display something if they
+  // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
+  volatile const DvrPoseAsync* buffer;
+  // Minimum number of accurate forecasted poses including the current frame's
+  // pose. This is the number of poses that are udpated by the pose service.
+  // If the application reads past this count, they will get a stale prediction
+  // from a previous frame. Guaranteed to be at least 2.
+  uint32_t min_future_count;
+  // Number of elements in buffer. At least 8 and greater than min_future_count.
+  // Guaranteed to be a power of two. The total size of the buffer in bytes is:
+  //   total_count * sizeof(DvrPoseAsync)
+  uint32_t total_count;
+} DvrPoseRingBufferInfo;
+
+typedef enum DvrPoseMode {
+  DVR_POSE_MODE_6DOF = 0,
+  DVR_POSE_MODE_3DOF,
+  DVR_POSE_MODE_MOCK_FROZEN,
+  DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
+  DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
+  DVR_POSE_MODE_MOCK_ROTATE_SLOW,
+  DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
+  DVR_POSE_MODE_MOCK_ROTATE_FAST,
+  DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
+
+  // Always last.
+  DVR_POSE_MODE_COUNT,
+} DvrPoseMode;
+
+typedef enum DvrControllerId {
+  DVR_CONTROLLER_0 = 0,
+  DVR_CONTROLLER_1 = 1,
+} DvrControllerId;
+
+// Creates a new pose client.
+//
+// @return Pointer to the created pose client, nullptr on failure.
+DvrPose* dvrPoseCreate();
+
+// Destroys a pose client.
+//
+// @param client Pointer to the pose client to be destroyed.
+void dvrPoseDestroy(DvrPose* client);
+
+// Gets the pose for the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+//     Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Gets the current vsync count.
+uint32_t dvrPoseGetVsyncCount(DvrPose* client);
+
+// Gets the pose for the given controller at the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param controller_id The controller id.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+//     Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+                         uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Enables/disables logging for the controller fusion.
+//
+// @param client Pointer to the pose client.
+// @param enable True starts logging, False stops.
+// @return Zero on success, negative error code on failure.
+int dvrPoseLogController(DvrPose* client, bool enable);
+
+// DEPRECATED
+// Polls current pose state.
+//
+// @param client Pointer to the pose client.
+// @param state Struct to store polled state.
+// @return Zero on success, negative error code on failure.
+int dvrPosePoll(DvrPose* client, DvrPoseState* state);
+
+// Freezes the pose to the provided state.
+//
+// Future poll operations will return this state until a different state is
+// frozen or dvrPoseSetMode() is called with a different mode. The timestamp is
+// not frozen.
+//
+// @param client Pointer to the pose client.
+// @param frozen_state State pose to be frozen to.
+// @return Zero on success, negative error code on failure.
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state);
+
+// Sets the pose service mode.
+//
+// @param mode The requested pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode);
+
+// Gets the pose service mode.
+//
+// @param mode Return value for the current pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode);
+
+// Get access to the shared memory pose ring buffer.
+// A future pose at vsync <current> + <offset> is accessed at index:
+//   index = (<current> + <offset>) % out_buffer_size
+// Where <current> was the last value returned by dvrPresent and
+// <offset> is less than or equal to |out_min_future_count|.
+// |out_buffer| will be set to a pointer to the buffer.
+// |out_fd| will be set to the gralloc buffer file descriptor, which is
+//   required for binding this buffer for GPU use.
+// Returns 0 on success.
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info);
+
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
new file mode 100644
index 0000000..40b4638
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_LATENCY_MODEL_H_
+#define ANDROID_DVR_LATENCY_MODEL_H_
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// This class models the latency from sensors. It will look at the first
+// window_size measurements and return their average after that.
+class LatencyModel {
+ public:
+  LatencyModel(size_t window_size);
+  ~LatencyModel() = default;
+
+  void AddLatency(int64_t latency_ns);
+  int64_t CurrentLatencyEstimate() const { return latency_; }
+
+ private:
+  size_t window_size_;
+  int64_t latency_sum_ = 0;
+  size_t num_summed_ = 0;
+  int64_t latency_ = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000..0616d46
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_POSE_IPC_H_
+#define ANDROID_DVR_POSE_IPC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DVR_POSE_SERVICE_BASE "system/vr/pose"
+#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
+
+enum {
+  DVR_POSE_POLL = 0,
+  DVR_POSE_FREEZE,
+  DVR_POSE_SET_MODE,
+  DVR_POSE_GET_RING_BUFFER,
+  DVR_POSE_NOTIFY_VSYNC,
+  DVR_POSE_GET_MODE,
+  DVR_POSE_GET_CONTROLLER_RING_BUFFER,
+  DVR_POSE_LOG_CONTROLLER,
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_IPC_H_
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..66c4c7c
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <stdint.h>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/sensor_constants.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sensord head pose ring buffer.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseRingBuffer {
+  // Ring buffer always at the beginning of the structure, as consumers may
+  // not have access to this parent structure definition.
+  DvrPoseAsync ring[kPoseAsyncBufferTotalCount];
+  // Current vsync_count (where sensord is writing poses from).
+  uint32_t vsync_count;
+} DvrPoseMetadata;
+
+// Called by displayd to give vsync count info to the pose service.
+// |display_timestamp| Display timestamp is in the middle of scanout.
+// |display_period_ns| Nanos between vsyncs.
+// |right_eye_photon_offset_ns| Nanos to shift the prediction timestamp for
+//    the right eye head pose (relative to the left eye prediction).
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+                              int64_t display_timestamp,
+                              int64_t display_period_ns,
+                              int64_t right_eye_photon_offset_ns);
+
+// Get file descriptor for access to the shared memory pose buffer. This can be
+// used with GL extensions that support shared memory buffer objects. The caller
+// takes ownership of the returned fd and must close it or pass on ownership.
+int privateDvrPoseGetRingBufferFd(DvrPose* client,
+                                  android::pdx::LocalHandle* fd);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000..b2ebd95
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_SENSOR_IPC_H_
+#define ANDROID_DVR_SENSOR_IPC_H_
+
+#define DVR_SENSOR_SERVICE_BASE "system/vr/sensors"
+
+#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client")
+
+/*
+ * Endpoint ops
+ */
+enum {
+  DVR_SENSOR_START = 0,
+  DVR_SENSOR_STOP,
+  DVR_SENSOR_POLL,
+};
+
+#endif  // ANDROID_DVR_SENSOR_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_client.h b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000..15a9b8f
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_SENSOR_CLIENT_H_
+#define ANDROID_DVR_SENSOR_CLIENT_H_
+
+#include <hardware/sensors.h>
+#include <pdx/client.h>
+#include <poll.h>
+
+namespace android {
+namespace dvr {
+
+// SensorClient is a remote interface to the sensor service in sensord.
+class SensorClient : public pdx::ClientBase<SensorClient> {
+ public:
+  ~SensorClient();
+
+  int StartSensor();
+  int StopSensor();
+  int Poll(sensors_event_t* events, int max_count);
+
+ private:
+  friend BASE;
+
+  // Set up a channel associated with the sensor of the indicated type.
+  // NOTE(segal): If our hardware ends up with multiple sensors of the same
+  // type, we'll have to change this.
+  explicit SensorClient(int sensor_type);
+
+  int sensor_type_;
+
+  SensorClient(const SensorClient&);
+  SensorClient& operator=(const SensorClient&);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSOR_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000..8fa87b3
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_SENSOR_CONSTANTS_H_
+#define ANDROID_DVR_SENSOR_CONSTANTS_H_
+
+namespace android {
+namespace dvr {
+
+// Number of elements in the async pose buffer.
+// Must be power of two.
+// Macro so that shader code can easily include this value.
+#define kPoseAsyncBufferTotalCount 8
+
+// Mask for accessing the current ring buffer array element:
+// index = vsync_count & kPoseAsyncBufferIndexMask
+constexpr uint32_t kPoseAsyncBufferIndexMask = kPoseAsyncBufferTotalCount - 1;
+
+// Number of pose frames including the current frame that are kept updated with
+// pose forecast data. The other poses are left their last known estimates.
+constexpr uint32_t kPoseAsyncBufferMinFutureCount = 4;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSOR_CONSTANTS_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
new file mode 100644
index 0000000..d3a4521
--- /dev/null
+++ b/libs/vr/libvrsensor/latency_model.cpp
@@ -0,0 +1,24 @@
+#include <private/dvr/latency_model.h>
+
+#include <cmath>
+
+namespace android {
+namespace dvr {
+
+LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
+
+void LatencyModel::AddLatency(int64_t latency_ns) {
+  // Not enough samples yet?
+  if (num_summed_ < window_size_) {
+    // Accumulate.
+    latency_sum_ += latency_ns;
+
+    // Have enough samples for latency estimate?
+    if (++num_summed_ == window_size_) {
+      latency_ = latency_sum_ / window_size_;
+    }
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
new file mode 100644
index 0000000..9eae3aa
--- /dev/null
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -0,0 +1,338 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+
+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 {
+
+// PoseClient is a remote interface to the pose service in sensord.
+class PoseClient : public pdx::ClientBase<PoseClient> {
+ public:
+  ~PoseClient() override {}
+
+  // Casts C handle into an instance of this class.
+  static PoseClient* FromC(DvrPose* client) {
+    return reinterpret_cast<PoseClient*>(client);
+  }
+
+  // Polls the pose service for the current state and stores it in *state.
+  // Returns zero on success, a negative error code otherwise.
+  int Poll(DvrPoseState* state) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state));
+    ALOGE_IF(!status, "Pose poll() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  uint32_t GetVsyncCount() {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return 0;
+    }
+    return mapped_pose_buffer_->vsync_count;
+  }
+
+  int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
+                        DvrPoseAsync* out_pose) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    if (!controllers_[controller_id].mapped_pose_buffer) {
+      int ret = GetControllerRingBuffer(controller_id);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        controllers_[controller_id]
+            .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  int LogController(bool enable) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
+                                         sizeof(enable), nullptr, 0);
+    ALOGE_IF(!status, "Pose LogController() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Freezes the pose to the provided state. Future poll operations will return
+  // this state until a different state is frozen or SetMode() is called with a
+  // different mode.
+  // Returns zero on success, a negative error code otherwise.
+  int Freeze(const DvrPoseState& frozen_state) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
+                                         sizeof(frozen_state), nullptr, 0);
+    ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Sets the data mode for the pose service.
+  int SetMode(DvrPoseMode mode) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
+    ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Gets the data mode for the pose service.
+  int GetMode(DvrPoseMode* out_mode) {
+    int mode;
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
+    ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    if (status)
+      *out_mode = DvrPoseMode(mode);
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
+    if (pose_buffer_.get()) {
+      if (out_info) {
+        GetPoseRingBufferInfo(out_info);
+      }
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status =
+        trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER);
+    if (!status) {
+      ALOGE("Pose GetRingBuffer() failed because: %s",
+            status.GetErrorMessage().c_str());
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    pose_buffer_.swap(buffer);
+    mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
+    ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+          mapped_pose_buffer_->ring[0].translation[0],
+          mapped_pose_buffer_->ring[0].translation[1],
+          mapped_pose_buffer_->ring[0].translation[2],
+          mapped_pose_buffer_->ring[0].orientation[0],
+          mapped_pose_buffer_->ring[0].orientation[1],
+          mapped_pose_buffer_->ring[0].orientation[2],
+          mapped_pose_buffer_->ring[0].orientation[3]);
+    if (out_info) {
+      GetPoseRingBufferInfo(out_info);
+    }
+    return 0;
+  }
+
+  int GetControllerRingBuffer(int32_t controller_id) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    ControllerClientState& client_state = controllers_[controller_id];
+    if (client_state.pose_buffer.get()) {
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+        DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
+        sizeof(controller_id), nullptr, 0);
+    if (!status) {
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync);
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    client_state.pose_buffer.swap(buffer);
+    client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
+    ALOGI(
+        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+        controller_id, client_state.mapped_pose_buffer[0].translation[0],
+        client_state.mapped_pose_buffer[0].translation[1],
+        client_state.mapped_pose_buffer[0].translation[2],
+        client_state.mapped_pose_buffer[0].orientation[0],
+        client_state.mapped_pose_buffer[0].orientation[1],
+        client_state.mapped_pose_buffer[0].orientation[2],
+        client_state.mapped_pose_buffer[0].orientation[3]);
+    return 0;
+  }
+
+  int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp,
+                  int64_t display_period_ns,
+                  int64_t right_eye_photon_offset_ns) {
+    const struct iovec data[] = {
+        {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)},
+        {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)},
+        {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)},
+        {.iov_base = &right_eye_photon_offset_ns,
+         .iov_len = sizeof(right_eye_photon_offset_ns)},
+    };
+    Transaction trans{*this};
+    Status<int> status =
+        trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr);
+    ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBufferFd(LocalHandle* fd) {
+    int ret = GetRingBuffer(nullptr);
+    if (ret < 0)
+      return ret;
+    *fd = pose_buffer_->GetBlobFd();
+    return 0;
+  }
+
+ private:
+  friend BASE;
+
+  // Set up a channel to the pose service.
+  PoseClient()
+      : BASE(pdx::default_transport::ClientChannelFactory::Create(
+            DVR_POSE_SERVICE_CLIENT)) {
+    // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
+    // block while waiting for the pose service to come back up.
+    EnableAutoReconnect(kInfiniteTimeout);
+  }
+
+  PoseClient(const PoseClient&) = delete;
+  PoseClient& operator=(const PoseClient&) = delete;
+
+  void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const {
+    out_info->min_future_count = kPoseAsyncBufferMinFutureCount;
+    out_info->total_count = kPoseAsyncBufferTotalCount;
+    out_info->buffer = mapped_pose_buffer_->ring;
+  }
+
+  std::unique_ptr<BufferConsumer> pose_buffer_;
+  const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr;
+
+  struct ControllerClientState {
+    std::unique_ptr<BufferConsumer> pose_buffer;
+    const DvrPoseAsync* mapped_pose_buffer = nullptr;
+  };
+  ControllerClientState controllers_[2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+using android::dvr::PoseClient;
+
+struct DvrPose {};
+
+extern "C" {
+
+DvrPose* dvrPoseCreate() {
+  PoseClient* client = PoseClient::Create().release();
+  return reinterpret_cast<DvrPose*>(client);
+}
+
+void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); }
+
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
+}
+
+uint32_t dvrPoseGetVsyncCount(DvrPose* client) {
+  return PoseClient::FromC(client)->GetVsyncCount();
+}
+
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+                         uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetControllerPose(controller_id,
+                                                      vsync_count, out_pose);
+}
+
+int dvrPoseLogController(DvrPose* client, bool enable) {
+  return PoseClient::FromC(client)->LogController(enable);
+}
+
+int dvrPosePoll(DvrPose* client, DvrPoseState* state) {
+  return PoseClient::FromC(client)->Poll(state);
+}
+
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) {
+  return PoseClient::FromC(client)->Freeze(*frozen_state);
+}
+
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) {
+  return PoseClient::FromC(client)->SetMode(mode);
+}
+
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) {
+  return PoseClient::FromC(client)->GetMode(mode);
+}
+
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) {
+  return PoseClient::FromC(client)->GetRingBuffer(out_info);
+}
+
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+                              int64_t display_timestamp,
+                              int64_t display_period_ns,
+                              int64_t right_eye_photon_offset_ns) {
+  return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp,
+                                                display_period_ns,
+                                                right_eye_photon_offset_ns);
+}
+
+int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) {
+  return PoseClient::FromC(client)->GetRingBufferFd(fd);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libvrsensor/sensor_client.cpp b/libs/vr/libvrsensor/sensor_client.cpp
new file mode 100644
index 0000000..04e88cc
--- /dev/null
+++ b/libs/vr/libvrsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/sensor-ipc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+SensorClient::SensorClient(int sensor_type)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DVR_SENSOR_SERVICE_CLIENT)),
+      sensor_type_(sensor_type) {}
+
+SensorClient::~SensorClient() {}
+
+int SensorClient::StartSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_,
+                                sizeof(sensor_type_), nullptr, 0);
+  ALOGE_IF(!status, "startSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::StopSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_STOP);
+  ALOGE_IF(!status, "stopSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::Poll(sensors_event_t* events, int max_events) {
+  int num_events = 0;
+  struct iovec rvec[] = {
+      {.iov_base = &num_events, .iov_len = sizeof(int)},
+      {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)},
+  };
+  Transaction trans{*this};
+  auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec);
+  ALOGE_IF(!status, "Sensor poll() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return !status ? -status.error() : num_events;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+// Entrypoints to simplify using the library when programmatically dynamicly
+// loading it.
+// Allows us to call this library without linking it, as, for instance,
+// when compiling GVR in Google3.
+// NOTE(segal): It's kind of a hack.
+
+extern "C" uint64_t dvrStartSensor(int type) {
+  android::dvr::SensorClient* service =
+      android::dvr::SensorClient::Create(type).release();
+  service->StartSensor();
+  return (uint64_t)service;
+}
+
+extern "C" void dvrStopSensor(uint64_t service) {
+  android::dvr::SensorClient* iss =
+      reinterpret_cast<android::dvr::SensorClient*>(service);
+  iss->StopSensor();
+  delete iss;
+}
+
+extern "C" int dvrPollSensor(uint64_t service, int max_count,
+                             sensors_event_t* events) {
+  return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll(
+      events, max_count);
+}
diff --git a/opengl/Android.bp b/opengl/Android.bp
new file mode 100644
index 0000000..c520bda
--- /dev/null
+++ b/opengl/Android.bp
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libEGL_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/EGL/**/*.h"],
+    license: "include/EGL/NOTICE",
+}
+
+ndk_headers {
+    name: "libGLESv1_CM_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES/**/*.h"],
+    license: "include/GLES/NOTICE",
+}
+
+ndk_headers {
+    name: "libGLESv2_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES2/**/*.h"],
+    license: "include/GLES2/NOTICE",
+}
+
+ndk_headers {
+    name: "libGLESv3_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES3/**/*.h"],
+    license: "include/GLES3/NOTICE",
+}
+
+ndk_headers {
+    name: "khr_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/KHR/**/*.h"],
+    license: "include/KHR/NOTICE",
+}
+
+subdirs = [
+    "*",
+]
diff --git a/opengl/include/EGL/NOTICE b/opengl/include/EGL/NOTICE
new file mode 100644
index 0000000..55f5efa
--- /dev/null
+++ b/opengl/include/EGL/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2007-2009 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
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 99ea342..ccb54ea 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -65,13 +65,13 @@
 #define EGL_TRUE			1
 
 /* Out-of-band handle values */
-#define EGL_DEFAULT_DISPLAY		((EGLNativeDisplayType)0)
-#define EGL_NO_CONTEXT			((EGLContext)0)
-#define EGL_NO_DISPLAY			((EGLDisplay)0)
-#define EGL_NO_SURFACE			((EGLSurface)0)
+#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			((EGLint)-1)
+#define EGL_DONT_CARE			EGL_CAST(EGLint, -1)
 
 /* Errors / GetError return values */
 #define EGL_SUCCESS			0x3000
@@ -198,7 +198,7 @@
 #define EGL_DISPLAY_SCALING		10000
 
 /* Unknown display resolution/aspect ratio */
-#define EGL_UNKNOWN			((EGLint)-1)
+#define EGL_UNKNOWN			EGL_CAST(EGLint, -1)
 
 /* Back buffer swap behaviors */
 #define EGL_BUFFER_PRESERVED		0x3094	/* EGL_SWAP_BEHAVIOR value */
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 2e18698..d0996f0 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -79,7 +79,7 @@
 #define EGL_KHR_image 1
 #define EGL_NATIVE_PIXMAP_KHR			0x30B0	/* eglCreateImageKHR target */
 typedef void *EGLImageKHR;
-#define EGL_NO_IMAGE_KHR			((EGLImageKHR)0)
+#define EGL_NO_IMAGE_KHR			EGL_CAST(EGLImageKHR, 0)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
 EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image);
@@ -115,6 +115,13 @@
 #define EGL_GL_TEXTURE_ZOFFSET_KHR		0x30BD	/* eglCreateImageKHR attribute */
 #endif
 
+#ifndef EGL_KHR_gl_colorspace
+#define EGL_KHR_gl_colorspace 1
+#define EGL_GL_COLORSPACE_KHR             0x309D
+#define EGL_GL_COLORSPACE_SRGB_KHR        0x3089
+#define EGL_GL_COLORSPACE_LINEAR_KHR      0x308A
+#endif
+
 #ifndef EGL_KHR_gl_renderbuffer_image
 #define EGL_KHR_gl_renderbuffer_image 1
 #define EGL_GL_RENDERBUFFER_KHR			0x30B9	/* eglCreateImageKHR target */
@@ -136,7 +143,7 @@
 #define EGL_SYNC_REUSABLE_KHR			0x30FA
 #define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR		0x0001	/* eglClientWaitSyncKHR <flags> bitfield */
 #define EGL_FOREVER_KHR				0xFFFFFFFFFFFFFFFFull
-#define EGL_NO_SYNC_KHR				((EGLSyncKHR)0)
+#define EGL_NO_SYNC_KHR				EGL_CAST(EGLSyncKHR, 0)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
 EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync);
@@ -213,7 +220,7 @@
 #define EGL_SYNC_TYPE_NV			0x30ED
 #define EGL_SYNC_CONDITION_NV			0x30EE
 #define EGL_SYNC_FENCE_NV			0x30EF
-#define EGL_NO_SYNC_NV				((EGLSyncNV)0)
+#define EGL_NO_SYNC_NV				EGL_CAST(EGLSyncNV, 0)
 typedef void* EGLSyncNV;
 typedef khronos_utime_nanoseconds_t EGLTimeNV;
 #ifdef EGL_EGLEXT_PROTOTYPES
@@ -339,7 +346,7 @@
 #define EGL_KHR_stream 1
 typedef void* EGLStreamKHR;
 typedef khronos_uint64_t EGLuint64KHR;
-#define EGL_NO_STREAM_KHR			((EGLStreamKHR)0)
+#define EGL_NO_STREAM_KHR			EGL_CAST(EGLStreamKHR, 0)
 #define EGL_CONSUMER_LATENCY_USEC_KHR		0x3210
 #define EGL_PRODUCER_FRAME_KHR			0x3212
 #define EGL_CONSUMER_FRAME_KHR			0x3213
@@ -466,7 +473,7 @@
 #ifndef EGL_KHR_stream_cross_process_fd
 #define EGL_KHR_stream_cross_process_fd 1
 typedef int EGLNativeFileDescriptorKHR;
-#define EGL_NO_FILE_DESCRIPTOR_KHR		((EGLNativeFileDescriptorKHR)(-1))
+#define EGL_NO_FILE_DESCRIPTOR_KHR		EGL_CAST(EGLNativeFileDescriptorKHR, -1)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLNativeFileDescriptorKHR EGLAPIENTRY eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream);
 EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor);
@@ -545,7 +552,7 @@
 #define EGL_SYNC_NATIVE_FENCE_ANDROID		0x3144
 #define EGL_SYNC_NATIVE_FENCE_FD_ANDROID	0x3145
 #define EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID	0x3146
-#define EGL_NO_NATIVE_FENCE_FD_ANDROID		-1
+#define EGL_NO_NATIVE_FENCE_FD_ANDROID		(-1)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLint EGLAPIENTRY eglDupNativeFenceFDANDROID( EGLDisplay dpy, EGLSyncKHR);
 #endif /* EGL_EGLEXT_PROTOTYPES */
@@ -598,16 +605,13 @@
 #endif
 #endif
 
-#ifndef EGL_ANDROID_create_native_client_buffer
-#define EGL_ANDROID_create_native_client_buffer 1
-#define EGL_NATIVE_BUFFER_USAGE_ANDROID   0x3143
-#define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID   0x00000001
-#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID   0x00000002
-#define EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID   0x00000004
+#ifndef EGL_ANDROID_get_native_client_buffer
+#define EGL_ANDROID_get_native_client_buffer 1
+struct AHardwareBuffer;
 #ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLClientBuffer eglCreateNativeClientBufferANDROID (const EGLint *attrib_list);
+EGLAPI EGLClientBuffer eglGetNativeClientBufferANDROID (const struct AHardwareBuffer *buffer);
 #else
-typedef EGLAPI EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (const EGLint *attrib_list);
+typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETNATIVECLIENTBUFFERANDROID) (const struct AHardwareBuffer *buffer);
 #endif
 #endif
 
@@ -623,22 +627,73 @@
 
 #ifndef EGL_ANDROID_get_frame_timestamps
 #define EGL_ANDROID_get_frame_timestamps 1
-#define EGL_TIMESTAMPS_ANDROID 0x314D
-#define EGL_QUEUE_TIME_ANDROID 0x314E
-#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#define EGL_TIMESTAMPS_ANDROID 0x3430
+#define EGL_COMPOSITE_DEADLINE_ANDROID 0x3431
+#define EGL_COMPOSITE_INTERVAL_ANDROID 0x3432
+#define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3433
+#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3434
+#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3435
+#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3436
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3437
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3438
+#define EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3439
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x343A
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x343B
+#define EGL_READS_DONE_TIME_ANDROID 0x343C
+#define EGL_TIMESTAMP_PENDING_ANDROID EGL_CAST(EGLnsecsANDROID, -2)
+#define EGL_TIMESTAMP_INVALID_ANDROID EGL_CAST(EGLnsecsANDROID, -1)
 #ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
-EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+EGLAPI EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+EGLAPI EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name);
+EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
 #else
-typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
-typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETNEXTFRAMEIDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
 #endif
 #endif
 
+#ifndef EGL_EXT_gl_colorspace_bt2020_linear
+#define EGL_EXT_gl_colorspace_bt2020_linear 1
+#define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F
+#endif /* EGL_EXT_gl_colorspace_bt2020_linear */
+
+#ifndef EGL_EXT_gl_colorspace_bt2020_pq
+#define EGL_EXT_gl_colorspace_bt2020_pq 1
+#define EGL_GL_COLORSPACE_BT2020_PQ_EXT   0x3340
+#endif /* EGL_EXT_gl_colorspace_bt2020_pq */
+
+#ifndef EGL_EXT_gl_colorspace_scrgb_linear
+#define EGL_EXT_gl_colorspace_scrgb_linear 1
+#define EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT 0x3350
+#endif /* EGL_EXT_gl_colorspace_scrgb_linear */
+
+#ifndef EGL_EXT_pixel_format_float
+#define EGL_EXT_pixel_format_float 1
+#define EGL_COLOR_COMPONENT_TYPE_EXT      0x3339
+#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A
+#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B
+#endif /* EGL_EXT_pixel_format_float */
+
+#ifndef EGL_EXT_surface_SMPTE2086_metadata
+#define EGL_EXT_surface_SMPTE2086_metadata 1
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT 0x3341
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT 0x3342
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT 0x3343
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT 0x3344
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT 0x3345
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT 0x3346
+#define EGL_SMPTE2086_WHITE_POINT_X_EXT   0x3347
+#define EGL_SMPTE2086_WHITE_POINT_Y_EXT   0x3348
+#define EGL_SMPTE2086_MAX_LUMINANCE_EXT   0x3349
+#define EGL_SMPTE2086_MIN_LUMINANCE_EXT   0x334A
+#define EGL_METADATA_SCALING_EXT          50000
+#endif /* EGL_EXT_surface_SMPTE2086_metadata */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 354ac22..54011c8 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -121,4 +121,10 @@
  */
 typedef khronos_int32_t EGLint;
 
+#if defined(__cplusplus)
+#define EGL_CAST(type, value) (static_cast<type>(value))
+#else
+#define EGL_CAST(type, value) ((type) (value))
+#endif
+
 #endif /* __eglplatform_h */
diff --git a/opengl/include/GLES/NOTICE b/opengl/include/GLES/NOTICE
new file mode 100644
index 0000000..4dc6614
--- /dev/null
+++ b/opengl/include/GLES/NOTICE
@@ -0,0 +1,2 @@
+This document is licensed under the SGI Free Software B License Version 2.0.
+For details, see http://oss.sgi.com/projects/FreeB/ .
diff --git a/opengl/include/GLES2/NOTICE b/opengl/include/GLES2/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES2/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 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
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/GLES3/NOTICE b/opengl/include/GLES3/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES3/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 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
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/KHR/NOTICE b/opengl/include/KHR/NOTICE
new file mode 100644
index 0000000..36796e8
--- /dev/null
+++ b/opengl/include/KHR/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2009 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
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/libagl/arch-mips/fixed_asm.S b/opengl/libagl/arch-mips/fixed_asm.S
index e1a53bc..a30ffc5 100644
--- a/opengl/libagl/arch-mips/fixed_asm.S
+++ b/opengl/libagl/arch-mips/fixed_asm.S
@@ -17,7 +17,7 @@
 
 
     .text
-    .align
+    .align 4
 
 /*
  * this version rounds-to-nearest and saturates numbers
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index c1efd1c..04f6d6d 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -18,21 +18,22 @@
 #include <assert.h>
 #include <atomic>
 #include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
 #include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <utils/threads.h>
 #include <ui/ANativeObjectBase.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -740,6 +741,7 @@
         case GGL_PIXEL_FORMAT_RGB_565:      size *= 2; break;
         case GGL_PIXEL_FORMAT_RGBA_8888:    size *= 4; break;
         case GGL_PIXEL_FORMAT_RGBX_8888:    size *= 4; break;
+        case GGL_PIXEL_FORMAT_BGRA_8888:    size *= 4; break;
         default:
             ALOGE("incompatible pixel format for pbuffer (format=%d)", f);
             pbuffer.data = 0;
@@ -1027,6 +1029,19 @@
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
+// BGRA 8888 config
+static config_pair_t const config_8_attribute_list[] = {
+        { EGL_BUFFER_SIZE,     32 },
+        { EGL_ALPHA_SIZE,       8 },
+        { EGL_BLUE_SIZE,        8 },
+        { EGL_GREEN_SIZE,       8 },
+        { EGL_RED_SIZE,         8 },
+        { EGL_DEPTH_SIZE,       0 },
+        { EGL_CONFIG_ID,        8 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_BGRA_8888 },
+        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
+};
+
 static configs_t const gConfigs[] = {
         { config_0_attribute_list, NELEM(config_0_attribute_list) },
         { config_1_attribute_list, NELEM(config_1_attribute_list) },
@@ -1036,6 +1051,7 @@
         { config_5_attribute_list, NELEM(config_5_attribute_list) },
         { config_6_attribute_list, NELEM(config_6_attribute_list) },
         { config_7_attribute_list, NELEM(config_7_attribute_list) },
+        { config_8_attribute_list, NELEM(config_8_attribute_list) },
 };
 
 static config_management_t const gConfigManagement[] = {
@@ -1118,6 +1134,10 @@
         pixelFormat = GGL_PIXEL_FORMAT_A_8;
         depthFormat = GGL_PIXEL_FORMAT_Z_16;
         break;
+    case 8:
+        pixelFormat = GGL_PIXEL_FORMAT_BGRA_8888;
+        depthFormat = 0;
+        break;
     default:
         return NAME_NOT_FOUND;
     }
@@ -1459,6 +1479,9 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
+    if (ggl_unlikely(num_config==NULL))
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+
     GLint numConfigs = NELEM(gConfigs);
     if (!configs) {
         *num_config = numConfigs;
@@ -1478,8 +1501,8 @@
 {
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    
-    if (ggl_unlikely(num_config==0)) {
+
+    if (ggl_unlikely(num_config==NULL)) {
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
new file mode 100644
index 0000000..d6bdc81
--- /dev/null
+++ b/opengl/libs/Android.bp
@@ -0,0 +1,171 @@
+// Build the ETC1 library
+cc_library {
+    name: "libETC1",
+    srcs: ["ETC1/etc1.cpp"],
+    host_supported: true,
+
+    target: {
+        android: {
+            static: {
+                enabled: false,
+            },
+        },
+        host: {
+            shared: {
+                enabled: false,
+            },
+        },
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+// The headers modules are in frameworks/native/opengl/Android.bp.
+ndk_library {
+    name: "libEGL",
+    symbol_file: "libEGL.map.txt",
+    first_version: "9",
+    unversioned_until: "current",
+}
+
+ndk_library {
+    name: "libGLESv1_CM",
+    symbol_file: "libGLESv1_CM.map.txt",
+    first_version: "9",
+    unversioned_until: "current",
+}
+
+ndk_library {
+    name: "libGLESv2",
+    symbol_file: "libGLESv2.map.txt",
+    first_version: "9",
+    unversioned_until: "current",
+}
+
+ndk_library {
+    name: "libGLESv3",
+    symbol_file: "libGLESv3.map.txt",
+    first_version: "18",
+    unversioned_until: "current",
+}
+
+cc_defaults {
+    name: "gl_libs_defaults",
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-fvisibility=hidden",
+    ],
+    shared_libs: [
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils or anything "above" libcutils
+        "libcutils",
+        "liblog",
+        "libdl",
+    ],
+
+    // we need to access the private Bionic header <bionic_tls.h>
+    include_dirs: ["bionic/libc/private"],
+}
+
+//##############################################################################
+// Build META EGL library
+//
+cc_defaults {
+    name: "egl_libs_defaults",
+    defaults: ["gl_libs_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"libEGL\"",
+    ],
+    shared_libs: [
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils nor anything "above" libui
+        "libui",
+        "libnativewindow",
+        "libbacktrace",
+    ],
+}
+
+cc_library_static {
+    name: "libEGL_getProcAddress",
+    defaults: ["egl_libs_defaults"],
+    srcs: ["EGL/getProcAddress.cpp"],
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+}
+
+cc_library_shared {
+    name: "libEGL",
+    defaults: ["egl_libs_defaults"],
+    srcs: [
+        "EGL/egl_tls.cpp",
+        "EGL/egl_cache.cpp",
+        "EGL/egl_display.cpp",
+        "EGL/egl_object.cpp",
+        "EGL/egl.cpp",
+        "EGL/eglApi.cpp",
+        "EGL/Loader.cpp",
+        "EGL/BlobCache.cpp",
+    ],
+    shared_libs: ["libvndksupport"],
+    static_libs: ["libEGL_getProcAddress"],
+    ldflags: ["-Wl,--exclude-libs=ALL"],
+    export_include_dirs: ["EGL/include"],
+}
+
+cc_test {
+    name: "libEGL_test",
+    defaults: ["egl_libs_defaults"],
+    srcs: [
+        "EGL/BlobCache.cpp",
+        "EGL/BlobCache_test.cpp",
+    ],
+}
+
+cc_defaults {
+    name: "gles_libs_defaults",
+    defaults: ["gl_libs_defaults"],
+    arch: {
+        arm: {
+            instruction_set: "arm",
+
+            // TODO: This is to work around b/20093774. Remove after root cause is fixed
+            ldflags: ["-Wl,--hash-style,both"],
+        },
+    },
+    shared_libs: ["libEGL"],
+}
+
+//##############################################################################
+// Build the wrapper OpenGL ES 1.x library
+//
+cc_library_shared {
+    name: "libGLESv1_CM",
+    defaults: ["gles_libs_defaults"],
+    srcs: ["GLES_CM/gl.cpp"],
+    cflags: ["-DLOG_TAG=\"libGLESv1\""],
+}
+
+//##############################################################################
+// Build the wrapper OpenGL ES 2.x library
+//
+cc_library_shared {
+    name: "libGLESv2",
+    defaults: ["gles_libs_defaults"],
+    srcs: ["GLES2/gl2.cpp"],
+    cflags: ["-DLOG_TAG=\"libGLESv2\""],
+}
+
+//##############################################################################
+// Build the wrapper OpenGL ES 3.x library (this is just different name for v2)
+//
+cc_library_shared {
+    name: "libGLESv3",
+    defaults: ["gles_libs_defaults"],
+    srcs: ["GLES2/gl2.cpp"],
+    cflags: ["-DLOG_TAG=\"libGLESv3\""],
+}
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 24e4c19..2f42ab6 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -1,182 +1 @@
 LOCAL_PATH:= $(call my-dir)
-
-###############################################################################
-# Build META EGL library
-#
-
-egl.cfg_config_module :=
-# OpenGL drivers config file
-ifneq ($(BOARD_EGL_CFG),)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := egl.cfg
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/egl
-LOCAL_SRC_FILES := ../../../../$(BOARD_EGL_CFG)
-include $(BUILD_PREBUILT)
-egl.cfg_config_module := $(LOCAL_MODULE)
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= 	       \
-	EGL/egl_tls.cpp        \
-	EGL/egl_cache.cpp      \
-	EGL/egl_display.cpp    \
-	EGL/egl_object.cpp     \
-	EGL/egl.cpp 	       \
-	EGL/eglApi.cpp 	       \
-	EGL/getProcAddress.cpp.arm \
-	EGL/Loader.cpp 	       \
-#
-
-LOCAL_SHARED_LIBRARIES += libbinder libcutils libutils liblog libui
-LOCAL_MODULE:= libEGL
-LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL
-LOCAL_SHARED_LIBRARIES += libdl
-# we need to access the private Bionic header <bionic_tls.h>
-LOCAL_C_INCLUDES += bionic/libc/private
-
-LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-LOCAL_CFLAGS += -fvisibility=hidden
-
-ifeq ($(BOARD_ALLOW_EGL_HIBERNATION),true)
-  LOCAL_CFLAGS += -DBOARD_ALLOW_EGL_HIBERNATION
-endif
-ifneq ($(MAX_EGL_CACHE_ENTRY_SIZE),)
-  LOCAL_CFLAGS += -DMAX_EGL_CACHE_ENTRY_SIZE=$(MAX_EGL_CACHE_ENTRY_SIZE)
-endif
-
-ifneq ($(MAX_EGL_CACHE_KEY_SIZE),)
-  LOCAL_CFLAGS += -DMAX_EGL_CACHE_KEY_SIZE=$(MAX_EGL_CACHE_KEY_SIZE)
-endif
-
-ifneq ($(MAX_EGL_CACHE_SIZE),)
-  LOCAL_CFLAGS += -DMAX_EGL_CACHE_SIZE=$(MAX_EGL_CACHE_SIZE)
-endif
-
-ifneq ($(filter address,$(SANITIZE_TARGET)),)
-  LOCAL_CFLAGS_32 += -DEGL_WRAPPER_DIR=\"/$(TARGET_COPY_OUT_DATA)/lib\"
-  LOCAL_CFLAGS_64 += -DEGL_WRAPPER_DIR=\"/$(TARGET_COPY_OUT_DATA)/lib64\"
-endif
-
-LOCAL_REQUIRED_MODULES := $(egl.cfg_config_module)
-egl.cfg_config_module :=
-
-include $(BUILD_SHARED_LIBRARY)
-
-###############################################################################
-# Build the wrapper OpenGL ES 1.x library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= 		\
-	GLES_CM/gl.cpp.arm 	\
-#
-
-LOCAL_CLANG := false
-LOCAL_SHARED_LIBRARIES += libcutils liblog libEGL
-LOCAL_MODULE:= libGLESv1_CM
-
-LOCAL_SHARED_LIBRARIES += libdl
-# we need to access the private Bionic header <bionic_tls.h>
-LOCAL_C_INCLUDES += bionic/libc/private
-
-LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv1\"
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-LOCAL_CFLAGS += -fvisibility=hidden
-
-# TODO: This is to work around b/20093774. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm += -Wl,--hash-style,both
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-###############################################################################
-# Build the wrapper OpenGL ES 2.x library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	GLES2/gl2.cpp   \
-#
-
-LOCAL_CLANG := false
-LOCAL_ARM_MODE := arm
-LOCAL_SHARED_LIBRARIES += libcutils libutils liblog libEGL
-LOCAL_MODULE:= libGLESv2
-
-LOCAL_SHARED_LIBRARIES += libdl
-# we need to access the private Bionic header <bionic_tls.h>
-LOCAL_C_INCLUDES += bionic/libc/private
-
-LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv2\"
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-LOCAL_CFLAGS += -fvisibility=hidden
-
-# TODO: This is to work around b/20093774. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm += -Wl,--hash-style,both
-
-include $(BUILD_SHARED_LIBRARY)
-
-###############################################################################
-# Build the wrapper OpenGL ES 3.x library (this is just different name for v2)
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	GLES2/gl2.cpp   \
-#
-
-LOCAL_CLANG := false
-LOCAL_ARM_MODE := arm
-LOCAL_SHARED_LIBRARIES += libcutils libutils liblog libEGL
-LOCAL_MODULE:= libGLESv3
-LOCAL_SHARED_LIBRARIES += libdl
-# we need to access the private Bionic header <bionic_tls.h>
-LOCAL_C_INCLUDES += bionic/libc/private
-
-LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv3\"
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-LOCAL_CFLAGS += -fvisibility=hidden
-
-# TODO: This is to work around b/20093774. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm += -Wl,--hash-style,both
-
-include $(BUILD_SHARED_LIBRARY)
-
-###############################################################################
-# Build the ETC1 host static library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= 		\
-	ETC1/etc1.cpp 	\
-#
-
-LOCAL_MODULE:= libETC1
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-###############################################################################
-# Build the ETC1 device library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= 		\
-	ETC1/etc1.cpp 	\
-#
-
-LOCAL_MODULE:= libETC1
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
new file mode 100644
index 0000000..f1b30c7
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -0,0 +1,373 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+
+#include "BlobCache.h"
+
+#include <inttypes.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <chrono>
+
+namespace android {
+
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 3;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+        mMaxKeySize(maxKeySize),
+        mMaxValueSize(maxValueSize),
+        mMaxTotalSize(maxTotalSize),
+        mTotalSize(0) {
+    int64_t now = std::chrono::steady_clock::now().time_since_epoch().count();
+#ifdef _WIN32
+    srand(now);
+#else
+    mRandState[0] = (now >> 0) & 0xFFFF;
+    mRandState[1] = (now >> 16) & 0xFFFF;
+    mRandState[2] = (now >> 32) & 0xFFFF;
+#endif
+    ALOGV("initializing random seed using %lld", (unsigned long long)now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
+                keySize, mMaxKeySize);
+        return;
+    }
+    if (mMaxValueSize < valueSize) {
+        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
+                valueSize, mMaxValueSize);
+        return;
+    }
+    if (mMaxTotalSize < keySize + valueSize) {
+        ALOGV("set: not caching because the combined key/value size is too "
+                "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+        return;
+    }
+    if (keySize == 0) {
+        ALOGW("set: not caching because keySize is 0");
+        return;
+    }
+    if (valueSize <= 0) {
+        ALOGW("set: not caching because valueSize is 0");
+        return;
+    }
+
+    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+
+    while (true) {
+        auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
+        if (index == mCacheEntries.end() || dummyEntry < *index) {
+            // Create a new cache entry.
+            std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true));
+            std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
+            size_t newTotalSize = mTotalSize + keySize + valueSize;
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    ALOGV("set: not caching new key/value pair because the "
+                            "total cache size limit would be exceeded: %zu "
+                            "(limit: %zu)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
+            mTotalSize = newTotalSize;
+            ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
+                    keySize, valueSize);
+        } else {
+            // Update the existing cache entry.
+            std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
+            std::shared_ptr<Blob> oldValueBlob(index->getValue());
+            size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    ALOGV("set: not caching new value because the total cache "
+                            "size limit would be exceeded: %zu (limit: %zu)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            index->setValue(valueBlob);
+            mTotalSize = newTotalSize;
+            ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
+                    "value", keySize, valueSize);
+        }
+        break;
+    }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
+                keySize, mMaxKeySize);
+        return 0;
+    }
+    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+    auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
+    if (index == mCacheEntries.end() || dummyEntry < *index) {
+        ALOGV("get: no cache entry found for key of size %zu", keySize);
+        return 0;
+    }
+
+    // The key was found. Return the value if the caller's buffer is large
+    // enough.
+    std::shared_ptr<Blob> valueBlob(index->getValue());
+    size_t valueBlobSize = valueBlob->getSize();
+    if (valueBlobSize <= valueSize) {
+        ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
+        memcpy(value, valueBlob->getData(), valueBlobSize);
+    } else {
+        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
+                valueSize, valueBlobSize);
+    }
+    return valueBlobSize;
+}
+
+static inline size_t align4(size_t size) {
+    return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+    size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
+    for (const CacheEntry& e :  mCacheEntries) {
+        std::shared_ptr<Blob> const& keyBlob = e.getKey();
+        std::shared_ptr<Blob> const& valueBlob = e.getValue();
+        size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
+    }
+    return size;
+}
+
+int BlobCache::flatten(void* buffer, size_t size) const {
+    // Write the cache header
+    if (size < sizeof(Header)) {
+        ALOGE("flatten: not enough room for cache header");
+        return 0;
+    }
+    Header* header = reinterpret_cast<Header*>(buffer);
+    header->mMagicNumber = blobCacheMagic;
+    header->mBlobCacheVersion = blobCacheVersion;
+    header->mDeviceVersion = blobCacheDeviceVersion;
+    header->mNumEntries = mCacheEntries.size();
+    char buildId[PROPERTY_VALUE_MAX];
+    header->mBuildIdLength = property_get("ro.build.id", buildId, "");
+    memcpy(header->mBuildId, buildId, header->mBuildIdLength);
+
+    // Write cache entries
+    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
+    for (const CacheEntry& e :  mCacheEntries) {
+        std::shared_ptr<Blob> const& keyBlob = e.getKey();
+        std::shared_ptr<Blob> const& valueBlob = e.getValue();
+        size_t keySize = keyBlob->getSize();
+        size_t valueSize = valueBlob->getSize();
+
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+        size_t totalSize = align4(entrySize);
+        if (byteOffset + totalSize > size) {
+            ALOGE("flatten: not enough room for cache entries");
+            return -EINVAL;
+        }
+
+        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(&byteBuffer[byteOffset]);
+        eheader->mKeySize = keySize;
+        eheader->mValueSize = valueSize;
+
+        memcpy(eheader->mData, keyBlob->getData(), keySize);
+        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+        if (totalSize > entrySize) {
+            // We have padding bytes. Those will get written to storage, and contribute to the CRC,
+            // so make sure we zero-them to have reproducible results.
+            memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
+        }
+
+        byteOffset += totalSize;
+    }
+
+    return 0;
+}
+
+int BlobCache::unflatten(void const* buffer, size_t size) {
+    // All errors should result in the BlobCache being in an empty state.
+    mCacheEntries.clear();
+
+    // Read the cache header
+    if (size < sizeof(Header)) {
+        ALOGE("unflatten: not enough room for cache header");
+        return -EINVAL;
+    }
+    const Header* header = reinterpret_cast<const Header*>(buffer);
+    if (header->mMagicNumber != blobCacheMagic) {
+        ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
+        return -EINVAL;
+    }
+    char buildId[PROPERTY_VALUE_MAX];
+    int len = property_get("ro.build.id", buildId, "");
+    if (header->mBlobCacheVersion != blobCacheVersion ||
+            header->mDeviceVersion != blobCacheDeviceVersion ||
+            len != header->mBuildIdLength ||
+            strncmp(buildId, header->mBuildId, len)) {
+        // We treat version mismatches as an empty cache.
+        return 0;
+    }
+
+    // Read cache entries
+    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
+    size_t numEntries = header->mNumEntries;
+    for (size_t i = 0; i < numEntries; i++) {
+        if (byteOffset + sizeof(EntryHeader) > size) {
+            mCacheEntries.clear();
+            ALOGE("unflatten: not enough room for cache entry headers");
+            return -EINVAL;
+        }
+
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+                &byteBuffer[byteOffset]);
+        size_t keySize = eheader->mKeySize;
+        size_t valueSize = eheader->mValueSize;
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+        size_t totalSize = align4(entrySize);
+        if (byteOffset + totalSize > size) {
+            mCacheEntries.clear();
+            ALOGE("unflatten: not enough room for cache entry headers");
+            return -EINVAL;
+        }
+
+        const uint8_t* data = eheader->mData;
+        set(data, keySize, data + keySize, valueSize);
+
+        byteOffset += totalSize;
+    }
+
+    return 0;
+}
+
+long int BlobCache::blob_random() {
+#ifdef _WIN32
+    return rand();
+#else
+    return nrand48(mRandState);
+#endif
+}
+
+void BlobCache::clean() {
+    // Remove a random cache entry until the total cache size gets below half
+    // the maximum total cache size.
+    while (mTotalSize > mMaxTotalSize / 2) {
+        size_t i = size_t(blob_random() % (mCacheEntries.size()));
+        const CacheEntry& entry(mCacheEntries[i]);
+        mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+        mCacheEntries.erase(mCacheEntries.begin() + i);
+    }
+}
+
+bool BlobCache::isCleanable() const {
+    return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
+        mData(copyData ? malloc(size) : data),
+        mSize(size),
+        mOwnsData(copyData) {
+    if (data != NULL && copyData) {
+        memcpy(const_cast<void*>(mData), data, size);
+    }
+}
+
+BlobCache::Blob::~Blob() {
+    if (mOwnsData) {
+        free(const_cast<void*>(mData));
+    }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+    if (mSize == rhs.mSize) {
+        return memcmp(mData, rhs.mData, mSize) < 0;
+    } else {
+        return mSize < rhs.mSize;
+    }
+}
+
+const void* BlobCache::Blob::getData() const {
+    return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+    return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(
+        const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
+        mKey(key),
+        mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+        mKey(ce.mKey),
+        mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+    return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+    mKey = rhs.mKey;
+    mValue = rhs.mValue;
+    return *this;
+}
+
+std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+    return mKey;
+}
+
+std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+    return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const std::shared_ptr<Blob>& value) {
+    mValue = value;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
new file mode 100644
index 0000000..a0a270a
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache.h
@@ -0,0 +1,245 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef ANDROID_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
+// does NOT provide any thread-safety guarantees.
+//
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program.  This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache {
+public:
+    // Create an empty blob cache. The blob cache will cache key/value pairs
+    // with key and value sizes less than or equal to maxKeySize and
+    // maxValueSize, respectively. The total combined size of ALL cache entries
+    // (key sizes plus value sizes) will not exceed maxTotalSize.
+    BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+    // set inserts a new binary value into the cache and associates it with the
+    // given binary key.  If the key or value are too large for the cache then
+    // the cache remains unchanged.  This includes the case where a different
+    // value was previously associated with the given key - the old value will
+    // remain in the cache.  If the given key and value are small enough to be
+    // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+    // values specified to the BlobCache constructor), then the key/value pair
+    // will be in the cache after set returns.  Note, however, that a subsequent
+    // call to set may evict old key/value pairs from the cache.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   value != NULL
+    //   0 < valueSize
+    void set(const void* key, size_t keySize, const void* value,
+            size_t valueSize);
+
+    // get retrieves from the cache the binary value associated with a given
+    // binary key.  If the key is present in the cache then the length of the
+    // binary value associated with that key is returned.  If the value argument
+    // is non-NULL and the size of the cached value is less than valueSize bytes
+    // then the cached value is copied into the buffer pointed to by the value
+    // argument.  If the key is not present in the cache then 0 is returned and
+    // the buffer pointed to by the value argument is not modified.
+    //
+    // Note that when calling get multiple times with the same key, the later
+    // calls may fail, returning 0, even if earlier calls succeeded.  The return
+    // value must be checked for each call.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   0 <= valueSize
+    size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+
+    // getFlattenedSize returns the number of bytes needed to store the entire
+    // serialized cache.
+    size_t getFlattenedSize() const;
+
+    // flatten serializes the current contents of the cache into the memory
+    // pointed to by 'buffer'.  The serialized cache contents can later be
+    // loaded into a BlobCache object using the unflatten method.  The contents
+    // of the BlobCache object will not be modified.
+    //
+    // Preconditions:
+    //   size >= this.getFlattenedSize()
+    int flatten(void* buffer, size_t size) const;
+
+    // unflatten replaces the contents of the cache with the serialized cache
+    // contents in the memory pointed to by 'buffer'.  The previous contents of
+    // the BlobCache will be evicted from the cache.  If an error occurs while
+    // unflattening the serialized cache contents then the BlobCache will be
+    // left in an empty state.
+    //
+    int unflatten(void const* buffer, size_t size);
+
+private:
+    // Copying is disallowed.
+    BlobCache(const BlobCache&);
+    void operator=(const BlobCache&);
+
+    // A random function helper to get around MinGW not having nrand48()
+    long int blob_random();
+
+    // clean evicts a randomly chosen set of entries from the cache such that
+    // the total size of all remaining entries is less than mMaxTotalSize/2.
+    void clean();
+
+    // isCleanable returns true if the cache is full enough for the clean method
+    // to have some effect, and false otherwise.
+    bool isCleanable() const;
+
+    // A Blob is an immutable sized unstructured data blob.
+    class Blob {
+    public:
+        Blob(const void* data, size_t size, bool copyData);
+        ~Blob();
+
+        bool operator<(const Blob& rhs) const;
+
+        const void* getData() const;
+        size_t getSize() const;
+
+    private:
+        // Copying is not allowed.
+        Blob(const Blob&);
+        void operator=(const Blob&);
+
+        // mData points to the buffer containing the blob data.
+        const void* mData;
+
+        // mSize is the size of the blob data in bytes.
+        size_t mSize;
+
+        // mOwnsData indicates whether or not this Blob object should free the
+        // memory pointed to by mData when the Blob gets destructed.
+        bool mOwnsData;
+    };
+
+    // A CacheEntry is a single key/value pair in the cache.
+    class CacheEntry {
+    public:
+        CacheEntry();
+        CacheEntry(const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value);
+        CacheEntry(const CacheEntry& ce);
+
+        bool operator<(const CacheEntry& rhs) const;
+        const CacheEntry& operator=(const CacheEntry&);
+
+        std::shared_ptr<Blob> getKey() const;
+        std::shared_ptr<Blob> getValue() const;
+
+        void setValue(const std::shared_ptr<Blob>& value);
+
+    private:
+
+        // mKey is the key that identifies the cache entry.
+        std::shared_ptr<Blob> mKey;
+
+        // mValue is the cached data associated with the key.
+        std::shared_ptr<Blob> mValue;
+    };
+
+    // A Header is the header for the entire BlobCache serialization format. No
+    // need to make this portable, so we simply write the struct out.
+    struct Header {
+        // mMagicNumber is the magic number that identifies the data as
+        // serialized BlobCache contents.  It must always contain 'Blb$'.
+        uint32_t mMagicNumber;
+
+        // mBlobCacheVersion is the serialization format version.
+        uint32_t mBlobCacheVersion;
+
+        // mDeviceVersion is the device-specific version of the cache.  This can
+        // be used to invalidate the cache.
+        uint32_t mDeviceVersion;
+
+        // mNumEntries is number of cache entries following the header in the
+        // data.
+        size_t mNumEntries;
+
+        // mBuildId is the build id of the device when the cache was created.
+        // When an update to the build happens (via an OTA or other update) this
+        // is used to invalidate the cache.
+        int mBuildIdLength;
+        char mBuildId[];
+    };
+
+    // An EntryHeader is the header for a serialized cache entry.  No need to
+    // make this portable, so we simply write the struct out.  Each EntryHeader
+    // is followed imediately by the key data and then the value data.
+    //
+    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+    // number of bytes that a serialized cache entry will occupy is:
+    //
+    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+    //
+    struct EntryHeader {
+        // mKeySize is the size of the entry key in bytes.
+        size_t mKeySize;
+
+        // mValueSize is the size of the entry value in bytes.
+        size_t mValueSize;
+
+        // mData contains both the key and value data for the cache entry.  The
+        // key comes first followed immediately by the value.
+        uint8_t mData[];
+    };
+
+    // mMaxKeySize is the maximum key size that will be cached. Calls to
+    // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxKeySize;
+
+    // mMaxValueSize is the maximum value size that will be cached. Calls to
+    // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxValueSize;
+
+    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+    // includes space for both keys and values. When a call to BlobCache::set
+    // would otherwise cause this limit to be exceeded, either the key/value
+    // pair passed to BlobCache::set will not be cached or other cache entries
+    // will be evicted from the cache to make room for the new entry.
+    const size_t mMaxTotalSize;
+
+    // mTotalSize is the total combined size of all keys and values currently in
+    // the cache.
+    size_t mTotalSize;
+
+    // mRandState is the pseudo-random number generator state. It is passed to
+    // nrand48 to generate random numbers when needed.
+    unsigned short mRandState[3];
+
+    // mCacheEntries stores all the cache entries that are resident in memory.
+    // Cache entries are added to it by the 'set' method.
+    std::vector<CacheEntry> mCacheEntries;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
new file mode 100644
index 0000000..edbaaf0
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -0,0 +1,434 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "BlobCache.h"
+
+namespace android {
+
+template<typename T> using sp = std::shared_ptr<T>;
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+
+    enum {
+        OK = 0,
+        BAD_VALUE = -EINVAL
+    };
+
+    enum {
+        MAX_KEY_SIZE = 6,
+        MAX_VALUE_SIZE = 8,
+        MAX_TOTAL_SIZE = 13,
+    };
+
+    virtual void SetUp() {
+        mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
+    }
+
+    virtual void TearDown() {
+        mBC.reset();
+    }
+
+    std::unique_ptr<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+    unsigned char buf[2] = { 0xee, 0xee };
+    mBC->set("ab", 2, "cd", 2);
+    mBC->set("ef", 2, "gh", 2);
+    ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+    ASSERT_EQ('c', buf[0]);
+    ASSERT_EQ('d', buf[1]);
+    ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+    ASSERT_EQ('g', buf[0]);
+    ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+    unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ('e', buf[1]);
+    ASSERT_EQ('f', buf[2]);
+    ASSERT_EQ('g', buf[3]);
+    ASSERT_EQ('h', buf[4]);
+    ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+    unsigned char buf[3] = { 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, "ijkl", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('i', buf[0]);
+    ASSERT_EQ('j', buf[1]);
+    ASSERT_EQ('k', buf[2]);
+    ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+    unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+    char key[MAX_KEY_SIZE+1];
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+    ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+    char buf[MAX_VALUE_SIZE+1];
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ(0xee, buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+    // Check a testing assumptions
+    ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+    char key[MAX_KEY_SIZE];
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+    ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+    ASSERT_EQ('w', buf[0]);
+    ASSERT_EQ('x', buf[1]);
+    ASSERT_EQ('y', buf[2]);
+    ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+    char buf[MAX_VALUE_SIZE];
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+            MAX_VALUE_SIZE));
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ('b', buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+    // Check a testing assumption
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+    unsigned char buf[1] = { 0xee };
+    mBC->set("x", 1, "y", 1);
+    ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+    ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    int numCached = 0;
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Insert one more entry, causing a cache overflow.
+    {
+        uint8_t k = maxEntries;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Count the number of entries in the cache.
+    int numCached = 0;
+    for (int i = 0; i < maxEntries+1; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+    virtual void SetUp() {
+        BlobCacheTest::SetUp();
+        mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
+    }
+
+    virtual void TearDown() {
+        mBC2.reset();
+        BlobCacheTest::TearDown();
+    }
+
+    void roundTrip() {
+        size_t size = mBC->getFlattenedSize();
+        uint8_t* flat = new uint8_t[size];
+        ASSERT_EQ(OK, mBC->flatten(flat, size));
+        ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+        delete[] flat;
+    }
+
+    sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    roundTrip();
+    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    roundTrip();
+
+    // Verify the deserialized cache
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    delete[] flat;
+
+    // Verify the cache that we just serialized
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize() - 1;
+    uint8_t* flat = new uint8_t[size];
+    // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+    // TODO: The above fails. I expect this is so because getFlattenedSize()
+    // overstimates the size by using PROPERTY_VALUE_MAX.
+    delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[1] = ~flat[1];
+
+    // Bad magic should cause an error.
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[5] = ~flat[5];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[10] = ~flat[10];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+
+    // A buffer truncation shouldt cause an error
+    // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+    // TODO: The above appears to fail because getFlattenedSize() is
+    // conservative.
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
new file mode 100644
index 0000000..0e2a9b3
--- /dev/null
+++ b/opengl/libs/EGL/CallStack.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <log/log.h>
+#include <backtrace/Backtrace.h>
+#include <memory>
+
+class CallStack {
+public:
+    // Create a callstack with the current thread's stack trace.
+    // Immediately dump it to logcat using the given logtag.
+    static void log(const char* logtag) noexcept {
+        std::unique_ptr<Backtrace> backtrace(
+                Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+        if (backtrace->Unwind(2)) {
+            for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
+                __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
+                        backtrace->FormatFrameData(i).c_str());
+            }
+        }
+    }
+};
+
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index ca5e6e8..823b502 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,25 +17,26 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <array>
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <dlfcn.h>
-#include <limits.h>
+#include "Loader.h"
+
+#include <string>
+
 #include <dirent.h>
+#include <dlfcn.h>
 
 #include <android/dlext.h>
-#include <cutils/log.h>
 #include <cutils/properties.h>
-#include <utils/Trace.h>
+#include <log/log.h>
 
-#include <EGL/egl.h>
+#include <ui/GraphicsEnv.h>
+#include <vndksupport/linker.h>
 
+#include "egl_trace.h"
 #include "egldefs.h"
-#include "Loader.h"
+
+extern "C" {
+  android_namespace_t* android_get_exported_namespace(const char*);
+}
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -65,7 +66,10 @@
  *
  */
 
-ANDROID_SINGLETON_STATIC_INSTANCE( Loader )
+Loader& Loader::getInstance() {
+    static Loader loader;
+    return loader;
+}
 
 /* This function is called to check whether we run inside the emulator,
  * and if this is the case whether GLES GPU emulation is supported.
@@ -102,29 +106,21 @@
     return atoi(prop);
 }
 
-// ----------------------------------------------------------------------------
-
-static char const * getProcessCmdline() {
-    long pid = getpid();
-    char procPath[128];
-    snprintf(procPath, 128, "/proc/%ld/cmdline", pid);
-    FILE * file = fopen(procPath, "r");
-    if (file) {
-        static char cmdline[256];
-        char *str = fgets(cmdline, sizeof(cmdline) - 1, file);
-        fclose(file);
-        if (str) {
-            return cmdline;
-        }
-    }
-    return NULL;
-}
-
 static void* do_dlopen(const char* path, int mode) {
     ATRACE_CALL();
     return dlopen(path, mode);
 }
 
+static void* do_android_dlopen_ext(const char* path, int mode, const android_dlextinfo* info) {
+    ATRACE_CALL();
+    return android_dlopen_ext(path, mode, info);
+}
+
+static void* do_android_load_sphal_library(const char* path, int mode) {
+    ATRACE_CALL();
+    return android_load_sphal_library(path, mode);
+}
+
 // ----------------------------------------------------------------------------
 
 Loader::driver_t::driver_t(void* gles)
@@ -144,7 +140,7 @@
     }
 }
 
-status_t Loader::driver_t::set(void* hnd, int32_t api)
+int Loader::driver_t::set(void* hnd, int32_t api)
 {
     switch (api) {
         case EGL:
@@ -157,34 +153,19 @@
             dso[2] = hnd;
             break;
         default:
-            return BAD_INDEX;
+            return -EOVERFLOW;
     }
-    return NO_ERROR;
+    return 0;
 }
 
 // ----------------------------------------------------------------------------
 
 Loader::Loader()
-    : getProcAddress(NULL),
-      mLibGui(nullptr),
-      mGetDriverNamespace(nullptr)
+    : getProcAddress(NULL)
 {
-    // FIXME: See note in GraphicsEnv.h about android_getDriverNamespace().
-    // libgui should already be loaded in any process that uses libEGL, but
-    // if for some reason it isn't, then we're not going to get a driver
-    // namespace anyway, so don't force it to be loaded.
-    mLibGui = dlopen("libgui.so", RTLD_NOLOAD | RTLD_LOCAL | RTLD_LAZY);
-    if (!mLibGui) {
-        ALOGD("failed to load libgui: %s", dlerror());
-        return;
-    }
-    mGetDriverNamespace = reinterpret_cast<decltype(mGetDriverNamespace)>(
-            dlsym(mLibGui, "android_getDriverNamespace"));
 }
 
 Loader::~Loader() {
-    if (mLibGui)
-        dlclose(mLibGui);
 }
 
 static void* load_wrapper(const char* path) {
@@ -270,11 +251,10 @@
     return (void*)hnd;
 }
 
-status_t Loader::close(void* driver)
+void Loader::close(void* driver)
 {
     driver_t* hnd = (driver_t*)driver;
     delete hnd;
-    return NO_ERROR;
 }
 
 void Loader::init_api(void* dso,
@@ -339,23 +319,23 @@
     ATRACE_CALL();
     class MatchFile {
     public:
-        static String8 find(const char* kind) {
-            String8 result;
+        static std::string find(const char* kind) {
+            std::string result;
             int emulationStatus = checkGlesEmulationStatus();
             switch (emulationStatus) {
                 case 0:
 #if defined(__LP64__)
-                    result.setTo("/system/lib64/egl/libGLES_android.so");
+                    result = "/system/lib64/egl/libGLES_android.so";
 #else
-                    result.setTo("/system/lib/egl/libGLES_android.so");
+                    result = "/system/lib/egl/libGLES_android.so";
 #endif
                     return result;
                 case 1:
                     // Use host-side OpenGL through the "emulation" library
 #if defined(__LP64__)
-                    result.appendFormat("/system/lib64/egl/lib%s_emulation.so", kind);
+                    result = std::string("/system/lib64/egl/lib") + kind + "_emulation.so";
 #else
-                    result.appendFormat("/system/lib/egl/lib%s_emulation.so", kind);
+                    result = std::string("/system/lib/egl/lib") + kind + "_emulation.so";
 #endif
                     return result;
                 default:
@@ -363,8 +343,7 @@
                     break;
             }
 
-            String8 pattern;
-            pattern.appendFormat("lib%s", kind);
+            std::string pattern = std::string("lib") + kind;
             const char* const searchPaths[] = {
 #if defined(__LP64__)
                     "/vendor/lib64/egl",
@@ -405,12 +384,11 @@
         }
 
     private:
-        static bool find(String8& result,
-                const String8& pattern, const char* const search, bool exact) {
+        static bool find(std::string& result,
+                const std::string& pattern, const char* const search, bool exact) {
             if (exact) {
-                String8 absolutePath;
-                absolutePath.appendFormat("%s/%s.so", search, pattern.string());
-                if (!access(absolutePath.string(), R_OK)) {
+                std::string absolutePath = std::string(search) + "/" + pattern;
+                if (!access(absolutePath.c_str(), R_OK)) {
                     result = absolutePath;
                     return true;
                 }
@@ -429,10 +407,9 @@
                         // always skip the software renderer
                         continue;
                     }
-                    if (strstr(e->d_name, pattern.string()) == e->d_name) {
+                    if (strstr(e->d_name, pattern.c_str()) == e->d_name) {
                         if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
-                            result.clear();
-                            result.appendFormat("%s/%s", search, e->d_name);
+                            result = std::string(search) + "/" + e->d_name;
                             closedir(d);
                             return true;
                         }
@@ -445,17 +422,22 @@
     };
 
 
-    String8 absolutePath = MatchFile::find(kind);
-    if (absolutePath.isEmpty()) {
+    std::string absolutePath = MatchFile::find(kind);
+    if (absolutePath.empty()) {
         // this happens often, we don't want to log an error
         return 0;
     }
-    const char* const driver_absolute_path = absolutePath.string();
+    const char* const driver_absolute_path = absolutePath.c_str();
 
-    void* dso = do_dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
+    // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
+    // the original routine when the namespace does not exist.
+    // See /system/core/rootdir/etc/ld.config.txt for the configuration of the
+    // sphal namespace.
+    void* dso = do_android_load_sphal_library(driver_absolute_path,
+                                              RTLD_NOW | RTLD_LOCAL);
     if (dso == 0) {
         const char* err = dlerror();
-        ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");
+        ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
         return 0;
     }
 
@@ -464,15 +446,10 @@
     return dso;
 }
 
-static void* do_android_dlopen_ext(const char* path, int mode, const android_dlextinfo* info) {
-    ATRACE_CALL();
-    return android_dlopen_ext(path, mode, info);
-}
-
-static const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
+static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
     "ro.hardware.egl",
     "ro.board.platform",
-}};
+};
 
 static void* load_updated_driver(const char* kind, android_namespace_t* ns) {
     ATRACE_CALL();
@@ -484,12 +461,11 @@
     char prop[PROPERTY_VALUE_MAX + 1];
     for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
         if (property_get(key, prop, nullptr) > 0) {
-            String8 name;
-            name.appendFormat("lib%s_%s.so", kind, prop);
-            so = do_android_dlopen_ext(name.string(), RTLD_LOCAL | RTLD_NOW,
-                    &dlextinfo);
-            if (so)
+            std::string name = std::string("lib") + kind + "_" + prop + ".so";
+            so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+            if (so) {
                 return so;
+            }
         }
     }
     return nullptr;
@@ -501,11 +477,9 @@
     ATRACE_CALL();
 
     void* dso = nullptr;
-    if (mGetDriverNamespace) {
-        android_namespace_t* ns = mGetDriverNamespace();
-        if (ns) {
-            dso = load_updated_driver(kind, ns);
-        }
+    android_namespace_t* ns = android_getDriverNamespace();
+    if (ns) {
+        dso = load_updated_driver(kind, ns);
     }
     if (!dso) {
         dso = load_system_driver(kind);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 04a8e41..6a32bb3 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -17,15 +17,7 @@
 #ifndef ANDROID_EGL_LOADER_H
 #define ANDROID_EGL_LOADER_H
 
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-#include <utils/String8.h>
-
-#include <gui/GraphicsEnv.h>
+#include <stdint.h>
 
 #include <EGL/egl.h>
 
@@ -35,12 +27,8 @@
 
 struct egl_connection_t;
 
-class Loader : public Singleton<Loader>
-{
-    friend class Singleton<Loader>;
-
-    typedef __eglMustCastToProperFunctionPointerType (*getProcAddressType)(
-            const char*);
+class Loader {
+    typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*);
    
     enum {
         EGL         = 0x01,
@@ -48,22 +36,21 @@
         GLESv2      = 0x04
     };
     struct driver_t {
-        driver_t(void* gles);
+        explicit driver_t(void* gles);
         ~driver_t();
-        status_t set(void* hnd, int32_t api);
+        // returns -errno
+        int set(void* hnd, int32_t api);
         void* dso[3];
     };
     
     getProcAddressType getProcAddress;
 
-    void* mLibGui;
-    decltype(android_getDriverNamespace)* mGetDriverNamespace;
-
 public:
+    static Loader& getInstance();
     ~Loader();
     
     void* open(egl_connection_t* cnx);
-    status_t close(void* driver);
+    void close(void* driver);
     
 private:
     Loader();
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 18cf261..f53cf3f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,31 +14,24 @@
  ** limitations under the License.
  */
 
-#include <ctype.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <hardware/gralloc.h>
-#include <system/window.h>
 
 #include <EGL/egl.h>
-#include <EGL/eglext.h>
 
-#include <cutils/log.h>
-#include <cutils/atomic.h>
 #include <cutils/properties.h>
 
-#include <utils/CallStack.h>
-#include <utils/String8.h>
+#include <log/log.h>
 
 #include "../egl_impl.h"
 
-#include "egl_tls.h"
 #include "egldefs.h"
-#include "Loader.h"
-
+#include "egl_tls.h"
 #include "egl_display.h"
 #include "egl_object.h"
+#include "CallStack.h"
+#include "Loader.h"
 
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
@@ -71,7 +64,7 @@
         char value[PROPERTY_VALUE_MAX];
         property_get("debug.egl.callstack", value, "0");
         if (atoi(value)) {
-            CallStack stack(LOG_TAG);
+            CallStack::log(LOG_TAG);
         }
     }
     return 0;
@@ -133,7 +126,7 @@
     if (name != GL_EXTENSIONS)
         return NULL;
 
-    return (const GLubyte *)c->gl_extensions.string();
+    return (const GLubyte *)c->gl_extensions.c_str();
 }
 
 const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
@@ -156,7 +149,7 @@
     if (index >= c->tokenized_gl_extensions.size())
         return NULL;
 
-    return (const GLubyte *)c->tokenized_gl_extensions.itemAt(index).string();
+    return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
 }
 
 GLint egl_get_num_extensions_for_current_context() {
@@ -213,14 +206,14 @@
 }
 
 static pthread_mutex_t sLogPrintMutex = PTHREAD_MUTEX_INITIALIZER;
-static nsecs_t sLogPrintTime = 0;
-#define NSECS_DURATION 1000000000
+static std::chrono::steady_clock::time_point sLogPrintTime;
+static constexpr std::chrono::seconds DURATION(1);
 
 void gl_unimplemented() {
     bool printLog = false;
-    nsecs_t now = systemTime();
+    auto now = std::chrono::steady_clock::now();
     pthread_mutex_lock(&sLogPrintMutex);
-    if ((now - sLogPrintTime) > NSECS_DURATION) {
+    if ((now - sLogPrintTime) > DURATION) {
         sLogPrintTime = now;
         printLog = true;
     }
@@ -230,7 +223,7 @@
         char value[PROPERTY_VALUE_MAX];
         property_get("debug.egl.callstack", value, "0");
         if (atoi(value)) {
-            CallStack stack(LOG_TAG);
+            CallStack::log(LOG_TAG);
         }
     }
 }
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 436ea30..ba3a5f9 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -16,56 +16,45 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <dlfcn.h>
 #include <ctype.h>
+#include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <hardware/gralloc.h>
-#include <system/window.h>
+#include <hardware/gralloc1.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <cutils/log.h>
-#include <cutils/atomic.h>
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
-#include <cutils/memory.h>
+#include <log/log.h>
 
-#include <gui/ISurfaceComposer.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include "binder/Binder.h"
-#include "binder/Parcel.h"
-#include "binder/IServiceManager.h"
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <unordered_map>
+#include <string>
+#include <thread>
 
 #include "../egl_impl.h"
-#include "../hooks.h"
 
 #include "egl_display.h"
 #include "egl_object.h"
 #include "egl_tls.h"
-#include "egldefs.h"
+#include "egl_trace.h"
 
 using namespace android;
 
-// This extension has not been ratified yet, so can't be shipped.
-// Implementation is incomplete and untested.
-#define ENABLE_EGL_KHR_GL_COLORSPACE 0
-
-#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
-
 // ----------------------------------------------------------------------------
 
 namespace android {
 
+using nsecs_t = int64_t;
+
 struct extention_map_t {
     const char* name;
     __eglMustCastToProperFunctionPointerType address;
@@ -86,24 +75,25 @@
  *
  * NOTE: Both strings MUST have a single space as the last character.
  */
-extern char const * const gBuiltinExtensionString =
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+char const * const gBuiltinExtensionString =
         "EGL_KHR_get_all_proc_addresses "
         "EGL_ANDROID_presentation_time "
         "EGL_KHR_swap_buffers_with_damage "
-        "EGL_ANDROID_create_native_client_buffer "
+        "EGL_ANDROID_get_native_client_buffer "
         "EGL_ANDROID_front_buffer_auto_refresh "
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
         "EGL_ANDROID_get_frame_timestamps "
-#endif
         ;
-extern char const * const gExtensionString  =
+
+char const * const gExtensionString  =
         "EGL_KHR_image "                        // mandatory
         "EGL_KHR_image_base "                   // mandatory
         "EGL_KHR_image_pixmap "
         "EGL_KHR_lock_surface "
-#if (ENABLE_EGL_KHR_GL_COLORSPACE != 0)
         "EGL_KHR_gl_colorspace "
-#endif
         "EGL_KHR_gl_texture_2D_image "
         "EGL_KHR_gl_texture_3D_image "
         "EGL_KHR_gl_texture_cubemap_image "
@@ -124,11 +114,14 @@
         "EGL_KHR_wait_sync "                    // strongly recommended
         "EGL_ANDROID_recordable "               // mandatory
         "EGL_KHR_partial_update "               // strongly recommended
+        "EGL_EXT_pixel_format_float "
         "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
         "EGL_KHR_create_context_no_error "
         "EGL_KHR_mutable_render_buffer "
         "EGL_EXT_yuv_surface "
         "EGL_EXT_protected_content "
+        "EGL_IMG_context_priority "
+        "EGL_KHR_no_config_context "
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -186,9 +179,9 @@
     { "eglSwapBuffersWithDamageKHR",
             (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
 
-    // EGL_ANDROID_native_client_buffer
-    { "eglCreateNativeClientBufferANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateNativeClientBufferANDROID },
+    // EGL_ANDROID_get_native_client_buffer
+    { "eglGetNativeClientBufferANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
 
     // EGL_KHR_partial_update
     { "eglSetDamageRegionKHR",
@@ -220,10 +213,20 @@
             (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
 
     // EGL_ANDROID_get_frame_timestamps
+    { "eglGetNextFrameIdANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+    { "eglGetCompositorTimingANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+    { "eglGetCompositorTimingSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
     { "eglGetFrameTimestampsANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
-    { "eglQueryTimestampSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
+    { "eglGetFrameTimestampSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+
+    // EGL_ANDROID_native_fence_sync
+    { "eglDupNativeFenceFDANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
 };
 
 /*
@@ -233,13 +236,13 @@
 #define FILTER_EXTENSIONS(procname) \
         (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
          !strcmp((procname), "eglHibernateProcessIMG")      ||    \
-         !strcmp((procname), "eglAwakenProcessIMG")         ||    \
-         !strcmp((procname), "eglDupNativeFenceFDANDROID"))
+         !strcmp((procname), "eglAwakenProcessIMG"))
 
 
 
 // accesses protected by sExtensionMapMutex
-static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
 static int sGLExtentionSlot = 0;
 static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -297,7 +300,7 @@
     clearError();
 
     egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->initialize(major, minor);
 
@@ -313,7 +316,7 @@
     clearError();
 
     egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->terminate();
 
@@ -334,7 +337,7 @@
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     EGLBoolean res = EGL_FALSE;
@@ -359,7 +362,7 @@
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     EGLBoolean res = EGL_FALSE;
@@ -391,6 +394,8 @@
                         case EGL_CONFIG_CAVEAT:
                             attribCaveat = &attrib_list[attribCount];
                             break;
+                        default:
+                            break;
                     }
                     attribCount++;
                 }
@@ -443,12 +448,6 @@
 // surfaces
 // ----------------------------------------------------------------------------
 
-// The EGL_KHR_gl_colorspace spec hasn't been ratified yet, so these haven't
-// been added to the Khronos egl.h.
-#define EGL_GL_COLORSPACE_KHR           EGL_VG_COLORSPACE
-#define EGL_GL_COLORSPACE_SRGB_KHR      EGL_VG_COLORSPACE_sRGB
-#define EGL_GL_COLORSPACE_LINEAR_KHR    EGL_VG_COLORSPACE_LINEAR
-
 // Turn linear formats into corresponding sRGB formats when colorspace is
 // EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear
 // formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where
@@ -474,8 +473,18 @@
     if (dp) {
         EGLDisplay iDpy = dp->disp.dpy;
 
+        if (!window) {
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
+        int value = 0;
+        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
+        if (!value) {
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
         int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-        if (result != OK) {
+        if (result < 0) {
             ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
                     "failed (%#x) (already connected to another API?)",
                     window, result);
@@ -488,26 +497,44 @@
         // modify the EGLconfig's format before setting the native window's
         // format.
 
-        // by default, just pick RGBA_8888
-        EGLint format = HAL_PIXEL_FORMAT_RGBA_8888;
-        android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
+        EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
+                                    &componentType);
 
+        EGLint format;
+        android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
         EGLint a = 0;
+        EGLint r, g, b;
+        r = g = b = 0;
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
         cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a);
-        if (a > 0) {
-            // alpha-channel requested, there's really only one suitable format
-            format = HAL_PIXEL_FORMAT_RGBA_8888;
-        } else {
-            EGLint r, g, b;
-            r = g = b = 0;
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
-            EGLint colorDepth = r + g + b;
+        EGLint colorDepth = r + g + b;
+
+        if (a == 0) {
             if (colorDepth <= 16) {
                 format = HAL_PIXEL_FORMAT_RGB_565;
             } else {
-                format = HAL_PIXEL_FORMAT_RGBX_8888;
+                if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                    if (colorDepth > 24) {
+                        format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                    } else {
+                        format = HAL_PIXEL_FORMAT_RGBX_8888;
+                    }
+                } else {
+                    format = HAL_PIXEL_FORMAT_RGBA_FP16;
+                }
+            }
+        } else {
+            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                if (colorDepth > 24) {
+                    format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    format = HAL_PIXEL_FORMAT_RGBA_8888;
+                }
+            } else {
+                format = HAL_PIXEL_FORMAT_RGBA_FP16;
             }
         }
 
@@ -515,17 +542,7 @@
         if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) {
             for (const EGLint* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
                 if (*attr == EGL_GL_COLORSPACE_KHR) {
-                    if (ENABLE_EGL_KHR_GL_COLORSPACE) {
-                        dataSpace = modifyBufferDataspace(dataSpace, *(attr+1));
-                    } else {
-                        // Normally we'd pass through unhandled attributes to
-                        // the driver. But in case the driver implements this
-                        // extension but we're disabling it, we want to prevent
-                        // it getting through -- support will be broken without
-                        // our help.
-                        ALOGE("sRGB window surfaces not supported");
-                        return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
-                    }
+                    dataSpace = modifyBufferDataspace(dataSpace, *(attr+1));
                 }
             }
         }
@@ -618,7 +635,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
     EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
@@ -638,7 +655,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     return s->cnx->egl.eglQuerySurface(
@@ -657,7 +674,6 @@
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return;
     }
 }
 
@@ -716,7 +732,7 @@
 
     ContextRef _c(dp.get(), ctx);
     if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
     EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
@@ -732,14 +748,14 @@
     clearError();
 
     egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
     // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
     // a valid but uninitialized display.
     if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
          (draw != EGL_NO_SURFACE) ) {
-        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
 
     // get a reference to the object passed in
@@ -750,7 +766,7 @@
     // validate the context (if not EGL_NO_CONTEXT)
     if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
         // EGL_NO_CONTEXT is valid
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
     }
 
     // these are the underlying implementation's object
@@ -773,7 +789,7 @@
         // no context given, use the implementation of the current context
         if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
             // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
+            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
         }
         if (cur_c == NULL) {
             // no current context
@@ -784,14 +800,14 @@
 
     // retrieve the underlying implementation's draw EGLSurface
     if (draw != EGL_NO_SURFACE) {
-        if (!_d.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
         d = get_surface(draw);
         impl_draw = d->surface;
     }
 
     // retrieve the underlying implementation's read EGLSurface
     if (read != EGL_NO_SURFACE) {
-        if (!_r.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
         r = get_surface(read);
         impl_read = r->surface;
     }
@@ -813,9 +829,41 @@
             egl_tls_t::setContext(EGL_NO_CONTEXT);
         }
     } else {
+
+        if (cur_c != NULL) {
+            // Force return to current context for drivers that cannot handle errors
+            EGLBoolean restore_result = EGL_FALSE;
+            // get a reference to the old current objects
+            ContextRef _c2(dp.get(), cur_c);
+            SurfaceRef _d2(dp.get(), cur_c->draw);
+            SurfaceRef _r2(dp.get(), cur_c->read);
+
+            c = cur_c;
+            impl_ctx = c->context;
+            impl_draw = EGL_NO_SURFACE;
+            if (cur_c->draw != EGL_NO_SURFACE) {
+                d = get_surface(cur_c->draw);
+                impl_draw = d->surface;
+            }
+            impl_read = EGL_NO_SURFACE;
+            if (cur_c->read != EGL_NO_SURFACE) {
+                r = get_surface(cur_c->read);
+                impl_read = r->surface;
+            }
+            restore_result = dp->makeCurrent(c, cur_c,
+                    cur_c->draw, cur_c->read, cur_c->context,
+                    impl_draw, impl_read, impl_ctx);
+            if (restore_result == EGL_TRUE) {
+                _c2.acquire();
+                _r2.acquire();
+                _d2.acquire();
+            } else {
+                ALOGE("Could not restore original EGL context");
+            }
+        }
         // this will ALOGE the error
         egl_connection_t* const cnx = &gEGLImpl;
-        result = setError(cnx->egl.eglGetError(), EGL_FALSE);
+        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
     }
     return result;
 }
@@ -830,7 +878,7 @@
     if (!dp) return EGL_FALSE;
 
     ContextRef _c(dp.get(), ctx);
-    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
     return c->cnx->egl.eglQueryContext(
@@ -891,7 +939,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitGL();
 }
@@ -902,7 +950,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitNative(engine);
 }
@@ -981,8 +1029,11 @@
          *
          */
 
-        const String8 name(procname);
-        addr = sGLExtentionMap.valueFor(name);
+        const std::string name(procname);
+
+    auto& extentionMap = sGLExtentionMap;
+    auto pos = extentionMap.find(name);
+        addr = (pos != extentionMap.end()) ? pos->second : nullptr;
         const int slot = sGLExtentionSlot;
 
         ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
@@ -1004,7 +1055,7 @@
 
             if (found) {
                 addr = gExtensionForwarders[slot];
-                sGLExtentionMap.add(name, addr);
+                extentionMap[name] = addr;
                 sGLExtentionSlot++;
             }
         }
@@ -1013,45 +1064,57 @@
     return addr;
 }
 
-class FrameCompletionThread : public Thread {
+class FrameCompletionThread {
 public:
 
     static void queueSync(EGLSyncKHR sync) {
-        static sp<FrameCompletionThread> thread(new FrameCompletionThread);
-        static bool running = false;
-        if (!running) {
-            thread->run("GPUFrameCompletion");
-            running = true;
-        }
-        {
-            Mutex::Autolock lock(thread->mMutex);
-            ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
-                    thread->mFramesQueued).string());
-            thread->mQueue.push_back(sync);
-            thread->mCondition.signal();
-            thread->mFramesQueued++;
-            ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
-        }
+        static FrameCompletionThread thread;
+
+        char name[64];
+
+        std::lock_guard<std::mutex> lock(thread.mMutex);
+        snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
+        ATRACE_NAME(name);
+
+        thread.mQueue.push_back(sync);
+        thread.mCondition.notify_one();
+        thread.mFramesQueued++;
+        ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
     }
 
 private:
-    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {}
 
-    virtual bool threadLoop() {
+    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
+        std::thread thread(&FrameCompletionThread::loop, this);
+        thread.detach();
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+    void loop() {
+        while (true) {
+            threadLoop();
+        }
+    }
+#pragma clang diagnostic pop
+
+    void threadLoop() {
         EGLSyncKHR sync;
         uint32_t frameNum;
         {
-            Mutex::Autolock lock(mMutex);
-            while (mQueue.isEmpty()) {
-                mCondition.wait(mMutex);
+            std::unique_lock<std::mutex> lock(mMutex);
+            while (mQueue.empty()) {
+                mCondition.wait(lock);
             }
             sync = mQueue[0];
             frameNum = mFramesCompleted;
         }
         EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
         {
-            ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
-                    frameNum).string());
+            char name[64];
+            snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
+            ATRACE_NAME(name);
+
             EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
             if (result == EGL_FALSE) {
                 ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
@@ -1061,19 +1124,18 @@
             eglDestroySyncKHR(dpy, sync);
         }
         {
-            Mutex::Autolock lock(mMutex);
-            mQueue.removeAt(0);
+            std::lock_guard<std::mutex> lock(mMutex);
+            mQueue.pop_front();
             mFramesCompleted++;
-            ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
         }
-        return true;
     }
 
     uint32_t mFramesQueued;
     uint32_t mFramesCompleted;
-    Vector<EGLSyncKHR> mQueue;
-    Condition mCondition;
-    Mutex mMutex;
+    std::deque<EGLSyncKHR> mQueue;
+    std::condition_variable mCondition;
+    std::mutex mMutex;
 };
 
 EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
@@ -1087,7 +1149,7 @@
 
     SurfaceRef _s(dp.get(), draw);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(draw);
 
@@ -1112,7 +1174,7 @@
         return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
     }
 
-    Vector<android_native_rect_t> androidRects;
+    std::vector<android_native_rect_t> androidRects((size_t)n_rects);
     for (int r = 0; r < n_rects; ++r) {
         int offset = r * 4;
         int x = rects[offset];
@@ -1126,8 +1188,7 @@
         androidRect.bottom = y;
         androidRects.push_back(androidRect);
     }
-    native_window_set_surface_damage(s->win.get(), androidRects.array(),
-            androidRects.size());
+    native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size());
 
     if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
         return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
@@ -1152,7 +1213,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
@@ -1162,6 +1223,16 @@
 {
     clearError();
 
+    // Generate an error quietly when client extensions (as defined by
+    // EGL_EXT_client_extensions) are queried.  We do not want to rely on
+    // validate_display to generate the error as validate_display would log
+    // the error, which can be misleading.
+    //
+    // If we want to support EGL_EXT_client_extensions later, we can return
+    // the client extension string here instead.
+    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
+        return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0);
+
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return (const char *) NULL;
 
@@ -1174,6 +1245,8 @@
             return dp->getExtensionString();
         case EGL_CLIENT_APIS:
             return dp->getClientApiString();
+        default:
+            break;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1194,6 +1267,8 @@
             return dp->disp.queryString.extensions;
         case EGL_CLIENT_APIS:
             return dp->disp.queryString.clientApi;
+        default:
+            break;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1212,29 +1287,31 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
-        int err = native_window_set_auto_refresh(s->win.get(),
-            value ? true : false);
-        return (err == NO_ERROR) ? EGL_TRUE :
+        if (!s->getNativeWindow()) {
             setError(EGL_BAD_SURFACE, EGL_FALSE);
+        }
+        int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
     if (attribute == EGL_TIMESTAMPS_ANDROID) {
-        s->enableTimestamps = value;
-        return EGL_TRUE;
+        if (!s->getNativeWindow()) {
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        }
+        int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
-#endif
 
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
                 dp->disp.dpy, s->surface, attribute, value);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglBindTexImage(
@@ -1247,14 +1324,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglBindTexImage) {
         return s->cnx->egl.eglBindTexImage(
                 dp->disp.dpy, s->surface, buffer);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglReleaseTexImage(
@@ -1267,14 +1344,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglReleaseTexImage) {
         return s->cnx->egl.eglReleaseTexImage(
                 dp->disp.dpy, s->surface, buffer);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
@@ -1304,7 +1381,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res;
     if (cnx->egl.eglWaitClient) {
@@ -1320,7 +1397,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     // bind this API on all EGLs
@@ -1337,7 +1414,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     egl_connection_t* const cnx = &gEGLImpl;
@@ -1353,13 +1430,14 @@
 {
     clearError();
 
-    // If there is context bound to the thread, release it
-    egl_display_t::loseCurrent(get_context(getContext()));
-
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglReleaseThread) {
         cnx->egl.eglReleaseThread();
     }
+
+    // If there is context bound to the thread, release it
+    egl_display_t::loseCurrent(get_context(getContext()));
+
     egl_tls_t::clearTLS();
     return EGL_TRUE;
 }
@@ -1394,14 +1472,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglLockSurfaceKHR) {
         return s->cnx->egl.eglLockSurfaceKHR(
                 dp->disp.dpy, s->surface, attrib_list);
     }
-    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
@@ -1413,13 +1491,13 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglUnlockSurfaceKHR) {
         return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
     }
-    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 }
 
 EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
@@ -1517,7 +1595,7 @@
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    EGLBoolean result = EGL_FALSE;
+    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
         result = cnx->egl.eglClientWaitSyncKHR(
@@ -1801,149 +1879,15 @@
     }
 
     egl_surface_t const * const s = get_surface(surface);
-    native_window_set_buffers_timestamp(s->win.get(), time);
+    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
 
     return EGL_TRUE;
 }
 
-EGLClientBuffer eglCreateNativeClientBufferANDROID(const EGLint *attrib_list)
-{
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
     clearError();
-
-    int usage = 0;
-    uint32_t width = 0;
-    uint32_t height = 0;
-    uint32_t format = 0;
-    uint32_t red_size = 0;
-    uint32_t green_size = 0;
-    uint32_t blue_size = 0;
-    uint32_t alpha_size = 0;
-
-#define GET_NONNEGATIVE_VALUE(case_name, target) \
-    case case_name: \
-        if (value >= 0) { \
-            target = value; \
-        } else { \
-            return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); \
-        } \
-        break
-
-    if (attrib_list) {
-        while (*attrib_list != EGL_NONE) {
-            GLint attr = *attrib_list++;
-            GLint value = *attrib_list++;
-            switch (attr) {
-                GET_NONNEGATIVE_VALUE(EGL_WIDTH, width);
-                GET_NONNEGATIVE_VALUE(EGL_HEIGHT, height);
-                GET_NONNEGATIVE_VALUE(EGL_RED_SIZE, red_size);
-                GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
-                GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
-                GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
-                case EGL_NATIVE_BUFFER_USAGE_ANDROID:
-                    if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
-                        usage |= GRALLOC_USAGE_PROTECTED;
-                    }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) {
-                        usage |= GRALLOC_USAGE_HW_RENDER;
-                    }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
-                        usage |= GRALLOC_USAGE_HW_TEXTURE;
-                    }
-                    break;
-                default:
-                    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-            }
-        }
-    }
-#undef GET_NONNEGATIVE_VALUE
-
-    // Validate format.
-    if (red_size == 8 && green_size == 8 && blue_size == 8) {
-        if (alpha_size == 8) {
-            format = HAL_PIXEL_FORMAT_RGBA_8888;
-        } else {
-            format = HAL_PIXEL_FORMAT_RGB_888;
-        }
-    } else if (red_size == 5 && green_size == 6 && blue_size == 5 &&
-               alpha_size == 0) {
-        format = HAL_PIXEL_FORMAT_RGB_565;
-    } else {
-        ALOGE("Invalid native pixel format { r=%d, g=%d, b=%d, a=%d }",
-                red_size, green_size, blue_size, alpha_size);
-        return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-    }
-
-#define CHECK_ERROR_CONDITION(message) \
-    if (err != NO_ERROR) { \
-        ALOGE(message); \
-        goto error_condition; \
-    }
-
-    // The holder is used to destroy the buffer if an error occurs.
-    GraphicBuffer* gBuffer = new GraphicBuffer();
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> surfaceFlinger = sm->getService(String16("SurfaceFlinger"));
-    sp<IBinder> allocator;
-    Parcel sc_data, sc_reply, data, reply;
-    status_t err = NO_ERROR;
-    if (sm == NULL) {
-        ALOGE("Unable to connect to ServiceManager");
-        goto error_condition;
-    }
-
-    // Obtain an allocator.
-    if (surfaceFlinger == NULL) {
-        ALOGE("Unable to connect to SurfaceFlinger");
-        goto error_condition;
-    }
-    sc_data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-    err = surfaceFlinger->transact(
-            BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, sc_data, &sc_reply);
-    CHECK_ERROR_CONDITION("Unable to obtain allocator from SurfaceFlinger");
-    allocator = sc_reply.readStrongBinder();
-
-    if (allocator == NULL) {
-        ALOGE("Unable to obtain an ISurfaceComposer");
-        goto error_condition;
-    }
-    data.writeInterfaceToken(String16("android.ui.IGraphicBufferAlloc"));
-    err = data.writeUint32(width);
-    CHECK_ERROR_CONDITION("Unable to write width");
-    err = data.writeUint32(height);
-    CHECK_ERROR_CONDITION("Unable to write height");
-    err = data.writeInt32(static_cast<int32_t>(format));
-    CHECK_ERROR_CONDITION("Unable to write format");
-    err = data.writeUint32(usage);
-    CHECK_ERROR_CONDITION("Unable to write usage");
-    err = data.writeUtf8AsUtf16(
-            std::string("[eglCreateNativeClientBufferANDROID pid ") +
-            std::to_string(getpid()) + ']');
-    CHECK_ERROR_CONDITION("Unable to write requestor name");
-    err = allocator->transact(IBinder::FIRST_CALL_TRANSACTION, data,
-            &reply);
-    CHECK_ERROR_CONDITION(
-            "Unable to request buffer allocation from surface composer");
-    err = reply.readInt32();
-    CHECK_ERROR_CONDITION("Unable to obtain buffer from surface composer");
-    err = reply.read(*gBuffer);
-    CHECK_ERROR_CONDITION("Unable to read buffer from surface composer");
-
-    err = gBuffer->initCheck();
-    if (err != NO_ERROR) {
-        ALOGE("Unable to create native buffer { w=%d, h=%d, f=%d, u=%#x }: %#x",
-                width, height, format, usage, err);
-        goto error_condition;
-    }
-    ALOGD("Created new native buffer %p { w=%d, h=%d, f=%d, u=%#x }",
-            gBuffer, width, height, format, usage);
-    return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
-
-#undef CHECK_ERROR_CONDITION
-
-error_condition:
-    // Delete the buffer.
-    sp<GraphicBuffer> holder(gBuffer);
-    return setError(EGL_BAD_ALLOC, (EGLClientBuffer)0);
+    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
+    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
 }
 
 // ----------------------------------------------------------------------------
@@ -1954,7 +1898,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
     EGLuint64NV ret = 0;
@@ -1964,7 +1908,7 @@
         return cnx->egl.eglGetSystemTimeFrequencyNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 EGLuint64NV eglGetSystemTimeNV()
@@ -1972,7 +1916,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
     EGLuint64NV ret = 0;
@@ -1982,7 +1926,7 @@
         return cnx->egl.eglGetSystemTimeNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 // ----------------------------------------------------------------------------
@@ -2014,103 +1958,258 @@
     return EGL_FALSE;
 }
 
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId) {
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    uint64_t nextFrameId = 0;
+    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
+
+    if (ret != 0) {
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetNextFrameId: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    *frameId = nextFrameId;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
+        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* compositeDeadline = nullptr;
+    nsecs_t* compositeInterval = nullptr;
+    nsecs_t* compositeToPresentLatency = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (names[i]) {
+            case EGL_COMPOSITE_DEADLINE_ANDROID:
+                compositeDeadline = &values[i];
+                break;
+            case EGL_COMPOSITE_INTERVAL_ANDROID:
+                compositeInterval = &values[i];
+                break;
+            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+                compositeToPresentLatency = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
+            compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+    switch (ret) {
+      case 0:
+        return EGL_TRUE;
+      case -ENOSYS:
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+      default:
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetCompositorTiming: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROID(
+        EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (name) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+            return EGL_TRUE;
+        default:
+            return EGL_FALSE;
+    }
+}
+
 EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
         EGLnsecsANDROID *values)
 {
     clearError();
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
     egl_surface_t const * const s = get_surface(surface);
 
-    if (!s->enableTimestamps) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    nsecs_t* postedTime = nullptr;
+    nsecs_t* requestedPresentTime = nullptr;
     nsecs_t* acquireTime = nullptr;
-    nsecs_t* refreshStartTime = nullptr;
-    nsecs_t* GLCompositionDoneTime = nullptr;
-    nsecs_t* displayRetireTime = nullptr;
+    nsecs_t* latchTime = nullptr;
+    nsecs_t* firstRefreshStartTime = nullptr;
+    nsecs_t* gpuCompositionDoneTime = nullptr;
+    nsecs_t* lastRefreshStartTime = nullptr;
+    nsecs_t* displayPresentTime = nullptr;
+    nsecs_t* dequeueReadyTime = nullptr;
     nsecs_t* releaseTime = nullptr;
 
     for (int i = 0; i < numTimestamps; i++) {
         switch (timestamps[i]) {
-            case EGL_QUEUE_TIME_ANDROID:
-                postedTime = &values[i];
+            case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+                requestedPresentTime = &values[i];
                 break;
             case EGL_RENDERING_COMPLETE_TIME_ANDROID:
                 acquireTime = &values[i];
                 break;
-            case EGL_COMPOSITION_START_TIME_ANDROID:
-                refreshStartTime = &values[i];
+            case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+                latchTime = &values[i];
                 break;
-            case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
-                GLCompositionDoneTime = &values[i];
+            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+                firstRefreshStartTime = &values[i];
                 break;
-            case EGL_DISPLAY_RETIRE_TIME_ANDROID:
-                displayRetireTime = &values[i];
+            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+                lastRefreshStartTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+                gpuCompositionDoneTime = &values[i];
+                break;
+            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+                displayPresentTime = &values[i];
+                break;
+            case EGL_DEQUEUE_READY_TIME_ANDROID:
+                dequeueReadyTime = &values[i];
                 break;
             case EGL_READS_DONE_TIME_ANDROID:
                 releaseTime = &values[i];
                 break;
             default:
-                setError(EGL_BAD_PARAMETER, EGL_FALSE);
-                return EGL_FALSE;
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
         }
     }
 
-    status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
-            postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
-            displayRetireTime, releaseTime);
+    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
+            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
+            dequeueReadyTime, releaseTime);
 
-    if (ret != NO_ERROR) {
-        setError(EGL_BAD_ACCESS, EGL_FALSE);
-        return EGL_FALSE;
+    switch (ret) {
+        case 0:
+            return EGL_TRUE;
+        case -ENOENT:
+            return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+        case -ENOSYS:
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        case -EINVAL:
+            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        default:
+            // This should not happen. Return an error that is not in the spec
+            // so it's obvious something is very wrong.
+            ALOGE("eglGetFrameTimestamps: Unexpected error.");
+            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
-
-    return EGL_TRUE;
 }
 
-EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint timestamp)
+EGLBoolean eglGetFrameTimestampSupportedANDROID(
+        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
 {
     clearError();
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
     switch (timestamp) {
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
-        case EGL_QUEUE_TIME_ANDROID:
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
         case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-        case EGL_COMPOSITION_START_TIME_ANDROID:
-        case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
-        case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+        case EGL_DEQUEUE_READY_TIME_ANDROID:
         case EGL_READS_DONE_TIME_ANDROID:
             return EGL_TRUE;
-#endif
+        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+            int value = 0;
+            window->query(window,
+                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+            return value == 0 ? EGL_FALSE : EGL_TRUE;
+        }
         default:
             return EGL_FALSE;
     }
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 8c135c8..dc1a4af 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -14,35 +14,27 @@
  ** limitations under the License.
  */
 
+#include "egl_cache.h"
+
 #include "../egl_impl.h"
 
-#include "egl_cache.h"
 #include "egl_display.h"
-#include "egldefs.h"
 
-#include <fcntl.h>
+
+#include <private/EGL/cache.h>
+
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#ifndef MAX_EGL_CACHE_ENTRY_SIZE
-#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
-#endif
+#include <thread>
 
-#ifndef MAX_EGL_CACHE_KEY_SIZE
-#define MAX_EGL_CACHE_KEY_SIZE (1024);
-#endif
-
-#ifndef MAX_EGL_CACHE_SIZE
-#define MAX_EGL_CACHE_SIZE (64 * 1024);
-#endif
+#include <log/log.h>
 
 // Cache size limits.
-static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE;
-static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
-static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
+static const size_t maxKeySize = 12 * 1024;
+static const size_t maxValueSize = 64 * 1024;
+static const size_t maxTotalSize = 2 * 1024 * 1024;
 
 // Cache file header
 static const char* cacheFileMagic = "EGL$";
@@ -57,6 +49,11 @@
 
 #define BC_EXT_STR "EGL_ANDROID_blob_cache"
 
+// called from android_view_ThreadedRenderer.cpp
+void egl_set_cache_filename(const char* filename) {
+    egl_cache_t::get()->setCacheFilename(filename);
+}
+
 //
 // Callback functions passed to EGL.
 //
@@ -74,8 +71,7 @@
 // egl_cache_t definition
 //
 egl_cache_t::egl_cache_t() :
-        mInitialized(false),
-        mBlobCache(NULL) {
+        mInitialized(false) {
 }
 
 egl_cache_t::~egl_cache_t() {
@@ -88,7 +84,7 @@
 }
 
 void egl_cache_t::initialize(egl_display_t *display) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
@@ -99,7 +95,7 @@
         bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
         bool atEnd = (bcExtLen+1) < extsLen &&
                 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
-        bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
+        bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
         if (equal || atStart || atEnd || inMiddle) {
             PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
             eglSetBlobCacheFuncsANDROID =
@@ -126,14 +122,14 @@
 }
 
 void egl_cache_t::terminate() {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     saveBlobCacheLocked();
     mBlobCache = NULL;
 }
 
 void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
         const void* value, EGLsizeiANDROID valueSize) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
@@ -141,38 +137,27 @@
     }
 
     if (mInitialized) {
-        sp<BlobCache> bc = getBlobCacheLocked();
+        BlobCache* bc = getBlobCacheLocked();
         bc->set(key, keySize, value, valueSize);
 
         if (!mSavePending) {
-            class DeferredSaveThread : public Thread {
-            public:
-                DeferredSaveThread() : Thread(false) {}
-
-                virtual bool threadLoop() {
-                    sleep(deferredSaveDelay);
-                    egl_cache_t* c = egl_cache_t::get();
-                    Mutex::Autolock lock(c->mMutex);
-                    if (c->mInitialized) {
-                        c->saveBlobCacheLocked();
-                    }
-                    c->mSavePending = false;
-                    return false;
-                }
-            };
-
-            // The thread will hold a strong ref to itself until it has finished
-            // running, so there's no need to keep a ref around.
-            sp<Thread> deferredSaveThread(new DeferredSaveThread());
             mSavePending = true;
-            deferredSaveThread->run("DeferredSaveThread");
+            std::thread deferredSaveThread([this]() {
+                sleep(deferredSaveDelay);
+                std::lock_guard<std::mutex> lock(mMutex);
+                if (mInitialized) {
+                    saveBlobCacheLocked();
+                }
+                mSavePending = false;
+            });
+            deferredSaveThread.detach();
         }
     }
 }
 
 EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
         void* value, EGLsizeiANDROID valueSize) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
@@ -180,23 +165,23 @@
     }
 
     if (mInitialized) {
-        sp<BlobCache> bc = getBlobCacheLocked();
+        BlobCache* bc = getBlobCacheLocked();
         return bc->get(key, keySize, value, valueSize);
     }
     return 0;
 }
 
 void egl_cache_t::setCacheFilename(const char* filename) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     mFilename = filename;
 }
 
-sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
-    if (mBlobCache == NULL) {
-        mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
+BlobCache* egl_cache_t::getBlobCacheLocked() {
+    if (mBlobCache == nullptr) {
+        mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize));
         loadBlobCacheLocked();
     }
-    return mBlobCache;
+    return mBlobCache.get();
 }
 
 static uint32_t crc32c(const uint8_t* buf, size_t len) {
@@ -219,7 +204,7 @@
     if (mFilename.length() > 0 && mBlobCache != NULL) {
         size_t cacheSize = mBlobCache->getFlattenedSize();
         size_t headerSize = cacheFileHeaderSize;
-        const char* fname = mFilename.string();
+        const char* fname = mFilename.c_str();
 
         // Try to create the file with no permissions so we can write it
         // without anyone trying to read it.
@@ -254,8 +239,8 @@
             return;
         }
 
-        status_t err = mBlobCache->flatten(buf + headerSize, cacheSize);
-        if (err != OK) {
+        int err = mBlobCache->flatten(buf + headerSize, cacheSize);
+        if (err < 0) {
             ALOGE("error writing cache contents: %s (%d)", strerror(-err),
                     -err);
             delete [] buf;
@@ -288,10 +273,10 @@
     if (mFilename.length() > 0) {
         size_t headerSize = cacheFileHeaderSize;
 
-        int fd = open(mFilename.string(), O_RDONLY, 0);
+        int fd = open(mFilename.c_str(), O_RDONLY, 0);
         if (fd == -1) {
             if (errno != ENOENT) {
-                ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
+                ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
                         strerror(errno), errno);
             }
             return;
@@ -336,8 +321,8 @@
             return;
         }
 
-        status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize);
-        if (err != OK) {
+        int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
+        if (err < 0) {
             ALOGE("error reading cache contents: %s (%d)", strerror(-err),
                     -err);
             munmap(buf, fileSize);
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 8760009..56360f0 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,9 +20,11 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <utils/BlobCache.h>
-#include <utils/String8.h>
-#include <utils/StrongPointer.h>
+#include "BlobCache.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -78,7 +80,7 @@
     // key/value blob pairs.  If the BlobCache object has not yet been created,
     // this will do so, loading the serialized cache contents from disk if
     // possible.
-    sp<BlobCache> getBlobCacheLocked();
+    BlobCache* getBlobCacheLocked();
 
     // saveBlobCache attempts to save the current contents of mBlobCache to
     // disk.
@@ -99,14 +101,14 @@
     // mBlobCache is the cache in which the key/value blob pairs are stored.  It
     // is initially NULL, and will be initialized by getBlobCacheLocked the
     // first time it's needed.
-    sp<BlobCache> mBlobCache;
+    std::unique_ptr<BlobCache> mBlobCache;
 
     // mFilename is the name of the file for storing cache contents in between
     // program invocations.  It is initialized to an empty string at
     // construction time, and can be set with the setCacheFilename method.  An
     // empty string indicates that the cache should not be saved to or restored
     // from disk.
-    String8 mFilename;
+    std::string mFilename;
 
     // mSavePending indicates whether or not a deferred save operation is
     // pending.  Each time a key/value pair is inserted into the cache via
@@ -117,7 +119,7 @@
 
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables. It must be locked whenever the member variables are accessed.
-    mutable Mutex mMutex;
+    mutable std::mutex mMutex;
 
     // sCache is the singleton egl_cache_t object.
     static egl_cache_t sCache;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index acd70d1..b696920 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -17,17 +17,18 @@
 #define __STDC_LIMIT_MACROS 1
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <string.h>
+#include "egl_display.h"
 
 #include "../egl_impl.h"
 
+#include <private/EGL/display.h>
+
 #include "egl_cache.h"
-#include "egl_display.h"
 #include "egl_object.h"
 #include "egl_tls.h"
+#include "egl_trace.h"
 #include "Loader.h"
 #include <cutils/properties.h>
-#include <utils/Trace.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -55,6 +56,11 @@
     return false;
 }
 
+int egl_get_init_count(EGLDisplay dpy) {
+    egl_display_t* eglDisplay = egl_display_t::get(dpy);
+    return eglDisplay ? eglDisplay->getRefsCount() : 0;
+}
+
 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
 
 egl_display_t::egl_display_t() :
@@ -75,18 +81,18 @@
 }
 
 void egl_display_t::addObject(egl_object_t* object) {
-    Mutex::Autolock _l(lock);
-    objects.add(object);
+    std::lock_guard<std::mutex> _l(lock);
+    objects.insert(object);
 }
 
 void egl_display_t::removeObject(egl_object_t* object) {
-    Mutex::Autolock _l(lock);
-    objects.remove(object);
+    std::lock_guard<std::mutex> _l(lock);
+    objects.erase(object);
 }
 
 bool egl_display_t::getObject(egl_object_t* object) const {
-    Mutex::Autolock _l(lock);
-    if (objects.indexOf(object) >= 0) {
+    std::lock_guard<std::mutex> _l(lock);
+    if (objects.find(object) != objects.end()) {
         if (object->getDisplay() == this) {
             object->incRef();
             return true;
@@ -104,7 +110,7 @@
 
 EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
 
-    Mutex::Autolock _l(lock);
+    std::lock_guard<std::mutex> _l(lock);
     ATRACE_CALL();
 
     // get our driver loader
@@ -125,24 +131,26 @@
 
 EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
 
-    {
-        Mutex::Autolock _rf(refLock);
-
+    { // scope for refLock
+        std::unique_lock<std::mutex> _l(refLock);
         refs++;
         if (refs > 1) {
             if (major != NULL)
                 *major = VERSION_MAJOR;
             if (minor != NULL)
                 *minor = VERSION_MINOR;
-            while(!eglIsInitialized) refCond.wait(refLock);
+            while(!eglIsInitialized) {
+                refCond.wait(_l);
+            }
             return EGL_TRUE;
         }
-
-        while(eglIsInitialized) refCond.wait(refLock);
+        while(eglIsInitialized) {
+            refCond.wait(_l);
+        }
     }
 
-    {
-        Mutex::Autolock _l(lock);
+    { // scope for lock
+        std::lock_guard<std::mutex> _l(lock);
 
         setGLHooksThreadSpecific(&gHooksNoContext);
 
@@ -179,20 +187,19 @@
         }
 
         // the query strings are per-display
-        mVendorString.setTo(sVendorString);
-        mVersionString.setTo(sVersionString);
-        mClientApiString.setTo(sClientApiString);
+        mVendorString = sVendorString;
+        mVersionString = sVersionString;
+        mClientApiString = sClientApiString;
 
-        mExtensionString.setTo(gBuiltinExtensionString);
+        mExtensionString = gBuiltinExtensionString;
         char const* start = gExtensionString;
         do {
             // length of the extension name
             size_t len = strcspn(start, " ");
             if (len) {
                 // NOTE: we could avoid the copy if we had strnstr.
-                const String8 ext(start, len);
-                if (findExtension(disp.queryString.extensions, ext.string(),
-                        len)) {
+                const std::string ext(start, len);
+                if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
                     mExtensionString.append(ext + " ");
                 }
                 // advance to the next extension name, skipping the space.
@@ -218,14 +225,12 @@
             *major = VERSION_MAJOR;
         if (minor != NULL)
             *minor = VERSION_MINOR;
-
-        mHibernation.setDisplayValid(true);
     }
 
-    {
-        Mutex::Autolock _rf(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _l(refLock);
         eglIsInitialized = true;
-        refCond.broadcast();
+        refCond.notify_all();
     }
 
     return EGL_TRUE;
@@ -233,8 +238,8 @@
 
 EGLBoolean egl_display_t::terminate() {
 
-    {
-        Mutex::Autolock _rl(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _rl(refLock);
         if (refs == 0) {
             /*
              * From the EGL spec (3.2):
@@ -254,8 +259,8 @@
 
     EGLBoolean res = EGL_FALSE;
 
-    {
-        Mutex::Autolock _l(lock);
+    { // scope for lock
+        std::lock_guard<std::mutex> _l(lock);
 
         egl_connection_t* const cnx = &gEGLImpl;
         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
@@ -268,19 +273,16 @@
             res = EGL_TRUE;
         }
 
-        mHibernation.setDisplayValid(false);
-
         // Reset the extension string since it will be regenerated if we get
         // reinitialized.
-        mExtensionString.setTo("");
+        mExtensionString.clear();
 
         // Mark all objects remaining in the list as terminated, unless
         // there are no reference to them, it which case, we're free to
         // delete them.
         size_t count = objects.size();
         ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
-        for (size_t i=0 ; i<count ; i++) {
-            egl_object_t* o = objects.itemAt(i);
+        for (auto o : objects) {
             o->destroy();
         }
 
@@ -288,10 +290,10 @@
         objects.clear();
     }
 
-    {
-        Mutex::Autolock _rl(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _rl(refLock);
         eglIsInitialized = false;
-        refCond.broadcast();
+        refCond.notify_all();
     }
 
     return res;
@@ -316,7 +318,7 @@
     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
 
     { // scope for the lock
-        Mutex::Autolock _l(lock);
+        std::lock_guard<std::mutex> _l(lock);
         cur_c->onLooseCurrent();
 
     }
@@ -342,22 +344,18 @@
     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
 
     { // scope for the lock
-        Mutex::Autolock _l(lock);
+        std::lock_guard<std::mutex> _l(lock);
         if (c) {
             result = c->cnx->egl.eglMakeCurrent(
                     disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 c->onMakeCurrent(draw, read);
-                if (!cur_c) {
-                    mHibernation.incWakeCount(HibernationMachine::STRONG);
-                }
             }
         } else {
             result = cur_c->cnx->egl.eglMakeCurrent(
                     disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 cur_c->onLooseCurrent();
-                mHibernation.decWakeCount(HibernationMachine::STRONG);
             }
         }
     }
@@ -378,65 +376,7 @@
     if (!nameLen) {
         nameLen = strlen(name);
     }
-    return findExtension(mExtensionString.string(), name, nameLen);
-}
-
-// ----------------------------------------------------------------------------
-
-bool egl_display_t::HibernationMachine::incWakeCount(WakeRefStrength strength) {
-    Mutex::Autolock _l(mLock);
-    ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX,
-             "Invalid WakeCount (%d) on enter\n", mWakeCount);
-
-    mWakeCount++;
-    if (strength == STRONG)
-        mAttemptHibernation = false;
-
-    if (CC_UNLIKELY(mHibernating)) {
-        ALOGV("Awakening\n");
-        egl_connection_t* const cnx = &gEGLImpl;
-
-        // These conditions should be guaranteed before entering hibernation;
-        // we don't want to get into a state where we can't wake up.
-        ALOGD_IF(!mDpyValid || !cnx->egl.eglAwakenProcessIMG,
-                 "Invalid hibernation state, unable to awaken\n");
-
-        if (!cnx->egl.eglAwakenProcessIMG()) {
-            ALOGE("Failed to awaken EGL implementation\n");
-            return false;
-        }
-        mHibernating = false;
-    }
-    return true;
-}
-
-void egl_display_t::HibernationMachine::decWakeCount(WakeRefStrength strength) {
-    Mutex::Autolock _l(mLock);
-    ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount);
-
-    mWakeCount--;
-    if (strength == STRONG)
-        mAttemptHibernation = true;
-
-    if (mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
-        egl_connection_t* const cnx = &gEGLImpl;
-        mAttemptHibernation = false;
-        if (mAllowHibernation && mDpyValid &&
-                cnx->egl.eglHibernateProcessIMG &&
-                cnx->egl.eglAwakenProcessIMG) {
-            ALOGV("Hibernating\n");
-            if (!cnx->egl.eglHibernateProcessIMG()) {
-                ALOGE("Failed to hibernate EGL implementation\n");
-                return;
-            }
-            mHibernating = true;
-        }
-    }
-}
-
-void egl_display_t::HibernationMachine::setDisplayValid(bool valid) {
-    Mutex::Autolock _l(mLock);
-    mDpyValid = valid;
+    return findExtension(mExtensionString.c_str(), name, nameLen);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 2d86295..661f47e 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -18,17 +18,18 @@
 #define ANDROID_EGL_DISPLAY_H
 
 
-#include <ctype.h>
 #include <stdint.h>
-#include <stdlib.h>
+#include <stddef.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_set>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
 #include <cutils/compiler.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-#include <utils/String8.h>
 
 #include "egldefs.h"
 #include "../hooks.h"
@@ -68,20 +69,6 @@
     // add reference to this object. returns true if this is a valid object.
     bool getObject(egl_object_t* object) const;
 
-    // These notifications allow the display to keep track of how many window
-    // surfaces exist, which it uses to decide whether to hibernate the
-    // underlying EGL implementation. They can be called by any thread without
-    // holding a lock, but must be called via egl_display_ptr to ensure
-    // proper hibernate/wakeup sequencing. If a surface destruction triggers
-    // hibernation, hibernation will be delayed at least until the calling
-    // thread's egl_display_ptr is destroyed.
-    void onWindowSurfaceCreated() {
-        mHibernation.incWakeCount(HibernationMachine::STRONG);
-    }
-    void onWindowSurfaceDestroyed() {
-        mHibernation.decWakeCount(HibernationMachine::STRONG);
-    }
-
     static egl_display_t* get(EGLDisplay dpy);
     static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
 
@@ -94,10 +81,10 @@
     inline bool isValid() const { return magic == '_dpy'; }
     inline bool isAlive() const { return isValid(); }
 
-    char const * getVendorString() const { return mVendorString.string(); }
-    char const * getVersionString() const { return mVersionString.string(); }
-    char const * getClientApiString() const { return mClientApiString.string(); }
-    char const * getExtensionString() const { return mExtensionString.string(); }
+    char const * getVendorString() const { return mVendorString.c_str(); }
+    char const * getVersionString() const { return mVersionString.c_str(); }
+    char const * getClientApiString() const { return mClientApiString.c_str(); }
+    char const * getExtensionString() const { return mExtensionString.c_str(); }
 
     bool haveExtension(const char* name, size_t nameLen = 0) const;
 
@@ -127,59 +114,17 @@
 
 private:
     friend class egl_display_ptr;
-    bool enter() { return mHibernation.incWakeCount(HibernationMachine::WEAK); }
-    void leave() { return mHibernation.decWakeCount(HibernationMachine::WEAK); }
 
             uint32_t                    refs;
             bool                        eglIsInitialized;
-    mutable Mutex                       lock, refLock;
-    mutable Condition                   refCond;
-            SortedVector<egl_object_t*> objects;
-            String8 mVendorString;
-            String8 mVersionString;
-            String8 mClientApiString;
-            String8 mExtensionString;
-
-    // HibernationMachine uses its own internal mutex to protect its own data.
-    // The owning egl_display_t's lock may be but is not required to be held
-    // when calling HibernationMachine methods. As a result, nothing in this
-    // class may call back up to egl_display_t directly or indirectly.
-    class HibernationMachine {
-    public:
-        // STRONG refs cancel (inc) or initiate (dec) a hibernation attempt
-        // the next time the wakecount reaches zero. WEAK refs don't affect
-        // whether a hibernation attempt will be made. Use STRONG refs only
-        // for infrequent/heavy changes that are likely to indicate the
-        // EGLDisplay is entering or leaving a long-term idle state.
-        enum WakeRefStrength {
-            WEAK   = 0,
-            STRONG = 1,
-        };
-
-        HibernationMachine(): mWakeCount(0), mHibernating(false),
-                mAttemptHibernation(false), mDpyValid(false),
-#if BOARD_ALLOW_EGL_HIBERNATION
-                mAllowHibernation(true)
-#else
-                mAllowHibernation(false)
-#endif
-        {}
-        ~HibernationMachine() {}
-
-        bool incWakeCount(WakeRefStrength strenth);
-        void decWakeCount(WakeRefStrength strenth);
-
-        void setDisplayValid(bool valid);
-
-    private:
-        Mutex      mLock;
-        int32_t    mWakeCount;
-        bool       mHibernating;
-        bool       mAttemptHibernation;
-        bool       mDpyValid;
-        const bool mAllowHibernation;
-    };
-    HibernationMachine mHibernation;
+    mutable std::mutex                  lock;
+    mutable std::mutex                  refLock;
+    mutable std::condition_variable     refCond;
+            std::unordered_set<egl_object_t*> objects;
+            std::string mVendorString;
+            std::string mVersionString;
+            std::string mClientApiString;
+            std::string mExtensionString;
 };
 
 // ----------------------------------------------------------------------------
@@ -190,13 +135,7 @@
 // as the egl_display_ptr exists.
 class egl_display_ptr {
 public:
-    explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {
-        if (mDpy) {
-            if (CC_UNLIKELY(!mDpy->enter())) {
-                mDpy = NULL;
-            }
-        }
-    }
+    explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {}
 
     // We only really need a C++11 move constructor, not a copy constructor.
     // A move constructor would save an enter()/leave() pair on every EGL API
@@ -208,17 +147,9 @@
     //     other.mDpy = NULL;
     // }
     //
-    egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {
-        if (mDpy) {
-            mDpy->enter();
-        }
-    }
+    egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {}
 
-    ~egl_display_ptr() {
-        if (mDpy) {
-            mDpy->leave();
-        }
-    }
+    ~egl_display_ptr() {}
 
     const egl_display_t* operator->() const { return mDpy; }
           egl_display_t* operator->()       { return mDpy; }
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2b56718..b587a16 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -81,6 +81,7 @@
 EGL_ENTRY(EGLClientBuffer, eglGetRenderBufferANDROID, EGLDisplay, EGLSurface)
 EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
 EGL_ENTRY(EGLClientBuffer, eglCreateNativeClientBufferANDROID, const EGLint *)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer *)
 
 /* NVIDIA extensions */
 
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index cfecf77..6238780 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -14,19 +14,10 @@
  ** limitations under the License.
  */
 
-#include <string>
+#include "egl_object.h"
+
 #include <sstream>
 
-#include <ctype.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <utils/threads.h>
-
-#include "egl_object.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -68,27 +59,25 @@
         EGLNativeWindowType win, EGLSurface surface,
         egl_connection_t const* cnx) :
     egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
-    enableTimestamps(false), connected(true)
+    connected(true)
 {
     if (win) {
-        getDisplay()->onWindowSurfaceCreated();
+        win->incStrong(this);
     }
 }
 
 egl_surface_t::~egl_surface_t() {
-    ANativeWindow* const window = win.get();
-    if (window != NULL) {
+    if (win != NULL) {
         disconnect();
-        getDisplay()->onWindowSurfaceDestroyed();
+        win->decStrong(this);
     }
 }
 
 void egl_surface_t::disconnect() {
-    ANativeWindow* const window = win.get();
-    if (window != NULL && connected) {
-        native_window_set_buffers_format(window, 0);
-        if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
-            ALOGW("EGLNativeWindowType %p disconnect failed", window);
+    if (win != NULL && connected) {
+        native_window_set_buffers_format(win, 0);
+        if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
+            ALOGW("EGLNativeWindowType %p disconnect failed", win);
         }
         connected = false;
     }
@@ -121,22 +110,20 @@
      * add the extensions always handled by the wrapper
      */
 
-    if (gl_extensions.isEmpty()) {
+    if (gl_extensions.empty()) {
         // call the implementation's glGetString(GL_EXTENSIONS)
         const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
-        gl_extensions.setTo(exts);
-        if (gl_extensions.find("GL_EXT_debug_marker") < 0) {
-            String8 temp("GL_EXT_debug_marker ");
-            temp.append(gl_extensions);
-            gl_extensions.setTo(temp);
+        gl_extensions = exts;
+        if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+            gl_extensions.insert(0, "GL_EXT_debug_marker ");
         }
 
         // tokenize the supported extensions for the glGetStringi() wrapper
         std::stringstream ss;
         std::string str;
-        ss << gl_extensions.string();
+        ss << gl_extensions;
         while (ss >> str) {
-            tokenized_gl_extensions.push(String8(str.c_str()));
+            tokenized_gl_extensions.push_back(str);
         }
     }
 }
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 97eda4c..8988905 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -18,19 +18,19 @@
 #define ANDROID_EGL_OBJECT_H
 
 #include <atomic>
-#include <ctype.h>
 #include <stdint.h>
-#include <stdlib.h>
+#include <stddef.h>
+
+#include <string>
+#include <vector>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <utils/threads.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
 #include <system/window.h>
 
+#include <log/log.h>
+
 #include "egl_display.h"
 
 // ----------------------------------------------------------------------------
@@ -48,7 +48,7 @@
     virtual void terminate();
 
 public:
-    egl_object_t(egl_display_t* display);
+    explicit egl_object_t(egl_display_t* display);
     void destroy();
 
     inline void incRef() { count.fetch_add(1, std::memory_order_relaxed); }
@@ -62,8 +62,8 @@
     template <typename N, typename T>
     class LocalRef {
         egl_object_t* ref;
-        LocalRef();
-        LocalRef(const LocalRef* rhs);
+        LocalRef() = delete;
+        LocalRef(const LocalRef* rhs) = delete;
     public:
         ~LocalRef();
         explicit LocalRef(egl_object_t* rhs);
@@ -135,11 +135,17 @@
             EGLNativeWindowType win, EGLSurface surface,
             egl_connection_t const* cnx);
 
+    ANativeWindow* getNativeWindow() { return win; }
+    ANativeWindow* getNativeWindow() const { return win; }
+
+    // Try to keep the order of these fields and size unchanged. It's not public API, but
+    // it's not hard to imagine native games accessing them.
     EGLSurface surface;
     EGLConfig config;
-    sp<ANativeWindow> win;
+private:
+    ANativeWindow* win;
+public:
     egl_connection_t const* cnx;
-    bool enableTimestamps;
 private:
     bool connected;
     void disconnect();
@@ -164,8 +170,8 @@
     EGLSurface draw;
     egl_connection_t const* cnx;
     int version;
-    String8 gl_extensions;
-    Vector<String8> tokenized_gl_extensions;
+    std::string gl_extensions;
+    std::vector<std::string> tokenized_gl_extensions;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index f3739aa..8508c5f 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -14,18 +14,13 @@
  ** limitations under the License.
  */
 
-#include <stdlib.h>
-#include <pthread.h>
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <utils/CallStack.h>
-
-#include <EGL/egl.h>
-
 #include "egl_tls.h"
 
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include "CallStack.h"
 
 namespace android {
 
@@ -33,7 +28,7 @@
 pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
 
 egl_tls_t::egl_tls_t()
-    : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) {
+    : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) {
 }
 
 const char *egl_tls_t::egl_strerror(EGLint err) {
@@ -78,7 +73,7 @@
             char value[PROPERTY_VALUE_MAX];
             property_get("debug.egl.callstack", value, "0");
             if (atoi(value)) {
-                CallStack stack(LOG_TAG);
+                CallStack::log(LOG_TAG);
             }
         }
         tls->error = error;
@@ -87,11 +82,12 @@
 
 bool egl_tls_t::logNoContextCall() {
     egl_tls_t* tls = getTLS();
-    if (tls->logCallWithNoContext == true) {
+    if (tls->logCallWithNoContext) {
         tls->logCallWithNoContext = false;
         return true;
     }
     return false;
+
 }
 
 egl_tls_t* egl_tls_t::getTLS() {
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 00eae0b..9feae68 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -17,11 +17,9 @@
 #ifndef ANDROID_EGL_TLS_H
 #define ANDROID_EGL_TLS_H
 
-#include <pthread.h>
-
 #include <EGL/egl.h>
 
-#include "egldefs.h"
+#include <pthread.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -36,7 +34,7 @@
 
     EGLint      error;
     EGLContext  ctx;
-    EGLBoolean  logCallWithNoContext;
+    bool        logCallWithNoContext;
 
     egl_tls_t();
     static void validateTLSKey();
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
new file mode 100644
index 0000000..7664de2
--- /dev/null
+++ b/opengl/libs/EGL/egl_trace.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if defined(__ANDROID__)
+
+#include <stdint.h>
+
+#include <cutils/trace.h>
+
+// See <cutils/trace.h> for more ATRACE_* macros.
+
+// ATRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x ## y
+#define PASTE(x, y) _PASTE(x,y)
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+
+// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
+
+namespace android {
+
+class EglScopedTrace {
+public:
+    inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
+        atrace_begin(mTag, name);
+    }
+
+    inline ~EglScopedTrace() {
+        atrace_end(mTag);
+    }
+
+private:
+    uint64_t mTag;
+};
+
+}; // namespace android
+
+#else // !__ANDROID__
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // __ANDROID__
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 336c264..c05e840 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -15,10 +15,10 @@
  */
 
 #include <ctype.h>
-#include <stdlib.h>
 #include <errno.h>
+#include <stdlib.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "egldefs.h"
 
diff --git a/opengl/libs/EGL/include/private/EGL/cache.h b/opengl/libs/EGL/include/private/EGL/cache.h
new file mode 100644
index 0000000..0a176a8
--- /dev/null
+++ b/opengl/libs/EGL/include/private/EGL/cache.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cutils/compiler.h>
+
+namespace android {
+
+ANDROID_API void egl_set_cache_filename(const char* filename);
+
+} // namespace android
diff --git a/opengl/libs/EGL/include/private/EGL/display.h b/opengl/libs/EGL/include/private/EGL/display.h
new file mode 100644
index 0000000..9560de2
--- /dev/null
+++ b/opengl/libs/EGL/include/private/EGL/display.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+
+#include <cutils/compiler.h>
+
+namespace android {
+
+ANDROID_API int egl_get_init_count(EGLDisplay dpy);
+
+} // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 6034a8e..f7fde96 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -15,12 +15,11 @@
  */
 
 #include <ctype.h>
-#include <string.h>
 #include <errno.h>
-
+#include <string.h>
 #include <sys/ioctl.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 
 #include "../hooks.h"
@@ -34,39 +33,65 @@
 
 #undef API_ENTRY
 #undef CALL_GL_API
+#undef CALL_GL_API_INTERNAL_CALL
+#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+#undef CALL_GL_API_INTERNAL_DO_RETURN
 #undef CALL_GL_API_RETURN
 
 #if USE_SLOW_BINDING
 
     #define API_ENTRY(_api) _api
 
-    #define CALL_GL_API(_api, ...)                                       \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                         \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
         if (_c) return _c->_api(__VA_ARGS__);
 
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE return 0;
+
+    // This stays blank, since void functions will implicitly return, and
+    // all of the other functions will return 0 based on the previous macro.
+    #define CALL_GL_API_INTERNAL_DO_RETURN
+
 #elif defined(__arm__)
 
     #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                              \
-         asm volatile(                                          \
-            GET_TLS(r12)                                        \
-            "ldr   r12, [r12, %[tls]] \n"                       \
-            "cmp   r12, #0            \n"                       \
-            "ldrne pc,  [r12, %[api]] \n"                       \
-            :                                                   \
-            : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
-              [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api))    \
-            : "r12"                                             \
-            );
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                 \
+        asm volatile(                                            \
+            GET_TLS(r12)                                         \
+            "ldr   r12, [r12, %[tls]] \n"                        \
+            "cmp   r12, #0            \n"                        \
+            "ldrne pc,  [r12, %[api]] \n"                        \
+            :                                                    \
+            : [tls] "J"(TLS_SLOT_OPENGL_API*4),                  \
+              [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+            : "r0", "r1", "r2", "r3", "r12"                      \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        asm volatile(                             \
+            "mov r0, #0 \n"                       \
+            :                                     \
+            :                                     \
+            : "r0"                                \
+        );
+
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        asm volatile(                      \
+            "bx lr \n"                     \
+            :                              \
+            :                              \
+            : "r0"                         \
+        );
 
 #elif defined(__aarch64__)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
         asm volatile(                                               \
             "mrs x16, tpidr_el0\n"                                  \
             "ldr x16, [x16, %[tls]]\n"                              \
@@ -77,121 +102,173 @@
             :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)),      \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "x16"                                                 \
+            : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x16" \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        asm volatile(                             \
+            "mov w0, wzr \n"                      \
+            :                                     \
+            :                                     \
+            : "w0"                                \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        asm volatile(                      \
+            "ret \n"                       \
+            :                              \
+            :                              \
+            :                              \
         );
 
 #elif defined(__i386__)
 
-    #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
-        register void** fn;                                         \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
         __asm__ volatile(                                           \
-            "mov %%gs:0, %[fn]\n"                                   \
-            "mov %P[tls](%[fn]), %[fn]\n"                           \
-            "test %[fn], %[fn]\n"                                   \
+            "mov %%gs:0, %%eax\n"                                   \
+            "mov %P[tls](%%eax), %%eax\n"                           \
+            "test %%eax, %%eax\n"                                   \
             "je 1f\n"                                               \
-            "jmp *%P[api](%[fn])\n"                                 \
+            "jmp *%P[api](%%eax)\n"                                 \
             "1:\n"                                                  \
-            : [fn] "=r" (fn)                                        \
+            :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)),        \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "cc"                                                  \
+            : "cc", "%eax"                                          \
             );
 
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        __asm__ volatile(                         \
+            "xor %%eax, %%eax\n"                  \
+            :                                     \
+            :                                     \
+            : "%eax"                              \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        __asm__ volatile(                  \
+            "ret\n"                        \
+            :                              \
+            :                              \
+            :                              \
+        );
+
 #elif defined(__x86_64__)
 
-    #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
-         register void** fn;                                        \
-         __asm__ volatile(                                          \
-            "mov %%fs:0, %[fn]\n"                                   \
-            "mov %P[tls](%[fn]), %[fn]\n"                           \
-            "test %[fn], %[fn]\n"                                   \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
+        __asm__ volatile(                                           \
+            "mov %%fs:0, %%rax\n"                                   \
+            "mov %P[tls](%%rax), %%rax\n"                           \
+            "test %%rax, %%rax\n"                                   \
             "je 1f\n"                                               \
-            "jmp *%P[api](%[fn])\n"                                 \
+            "jmp *%P[api](%%rax)\n"                                 \
             "1:\n"                                                  \
-            : [fn] "=r" (fn)                                        \
+            :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)),        \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "cc"                                                  \
-            );
+            : "cc", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9",   \
+              "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", \
+              "%xmm6", "%xmm7"                                      \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        __asm__ volatile(                         \
+            "xor %%eax, %%eax\n"                  \
+            :                                     \
+            :                                     \
+            : "%eax"                              \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        __asm__ volatile(                  \
+            "retq\n"                       \
+            :                              \
+            :                              \
+            :                              \
+        );
 
 #elif defined(__mips64)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                            \
-    register unsigned long _t0 asm("$12");                    \
-    register unsigned long _fn asm("$25");                    \
-    register unsigned long _tls asm("$3");                    \
-    register unsigned long _v0 asm("$2");                     \
-    asm volatile(                                             \
-        ".set  push\n\t"                                      \
-        ".set  noreorder\n\t"                                 \
-        "rdhwr %[tls], $29\n\t"                               \
-        "ld    %[t0], %[OPENGL_API](%[tls])\n\t"              \
-        "beqz  %[t0], 1f\n\t"                                 \
-        " move %[fn], $ra\n\t"                                \
-        "ld    %[t0], %[API](%[t0])\n\t"                      \
-        "beqz  %[t0], 1f\n\t"                                 \
-        " nop\n\t"                                            \
-        "move  %[fn], %[t0]\n\t"                              \
-        "1:\n\t"                                              \
-        "jalr  $0, %[fn]\n\t"                                 \
-        " move %[v0], $0\n\t"                                 \
-        ".set  pop\n\t"                                       \
-        : [fn] "=c"(_fn),                                     \
-          [tls] "=&r"(_tls),                                  \
-          [t0] "=&r"(_t0),                                    \
-          [v0] "=&r"(_v0)                                     \
-        : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
-          [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api))  \
-        :                                                     \
+    // t0:  $12
+    // fn:  $25
+    // tls: $3
+    // v0:  $2
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                  \
+        asm volatile(                                             \
+            ".set  push\n\t"                                      \
+            ".set  noreorder\n\t"                                 \
+            "rdhwr $3, $29\n\t"                                   \
+            "ld    $12, %[OPENGL_API]($3)\n\t"                    \
+            "beqz  $12, 1f\n\t"                                   \
+            " move $25, $ra\n\t"                                  \
+            "ld    $12, %[API]($12)\n\t"                          \
+            "beqz  $12, 1f\n\t"                                   \
+            " nop\n\t"                                            \
+            "move  $25, $12\n\t"                                  \
+            "1:\n\t"                                              \
+            "jalr  $0, $25\n\t"                                   \
+            " move $2, $0\n\t"                                    \
+            ".set  pop\n\t"                                       \
+            :                                                     \
+            : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
+              [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api))  \
+            : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",     \
+              "$10", "$11", "$12", "$25"                          \
         );
 
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+    #define CALL_GL_API_INTERNAL_DO_RETURN
+
 #elif defined(__mips__)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                               \
-        register unsigned int _t0 asm("$8");                     \
-        register unsigned int _fn asm("$25");                    \
-        register unsigned int _tls asm("$3");                    \
-        register unsigned int _v0 asm("$2");                     \
+    // t0:  $8
+    // fn:  $25
+    // tls: $3
+    // v0:  $2
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                 \
         asm volatile(                                            \
             ".set  push\n\t"                                     \
             ".set  noreorder\n\t"                                \
             ".set  mips32r2\n\t"                                 \
-            "rdhwr %[tls], $29\n\t"                              \
-            "lw    %[t0], %[OPENGL_API](%[tls])\n\t"             \
-            "beqz  %[t0], 1f\n\t"                                \
-            " move %[fn],$ra\n\t"                                \
-            "lw    %[t0], %[API](%[t0])\n\t"                     \
-            "beqz  %[t0], 1f\n\t"                                \
+            "rdhwr $3, $29\n\t"                                  \
+            "lw    $3, %[OPENGL_API]($3)\n\t"                    \
+            "beqz  $3, 1f\n\t"                                   \
+            " move $25,$ra\n\t"                                  \
+            "lw    $3, %[API]($3)\n\t"                           \
+            "beqz  $3, 1f\n\t"                                   \
             " nop\n\t"                                           \
-            "move  %[fn], %[t0]\n\t"                             \
+            "move  $25, $3\n\t"                                  \
             "1:\n\t"                                             \
-            "jalr  $0, %[fn]\n\t"                                \
-            " move %[v0], $0\n\t"                                \
+            "jalr  $0, $25\n\t"                                  \
+            " move $2, $0\n\t"                                   \
             ".set  pop\n\t"                                      \
-            : [fn] "=c"(_fn),                                    \
-              [tls] "=&r"(_tls),                                 \
-              [t0] "=&r"(_t0),                                   \
-              [v0] "=&r"(_v0)                                    \
+            :                                                    \
             : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4),           \
               [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
-            :                                                    \
-            );
+            : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25"    \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+    #define CALL_GL_API_INTERNAL_DO_RETURN
 
 #endif
 
+#define CALL_GL_API(_api, ...) \
+    CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \
+    CALL_GL_API_INTERNAL_DO_RETURN
+
 #define CALL_GL_API_RETURN(_api, ...) \
-    CALL_GL_API(_api, __VA_ARGS__) \
-    return 0;
-
-
+    CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \
+    CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+    CALL_GL_API_INTERNAL_DO_RETURN
 
 extern "C" {
 #pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -202,6 +279,9 @@
 
 #undef API_ENTRY
 #undef CALL_GL_API
+#undef CALL_GL_API_INTERNAL_CALL
+#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+#undef CALL_GL_API_INTERNAL_DO_RETURN
 #undef CALL_GL_API_RETURN
 
 /*
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index b1b31f8..bacd4b4 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -1,31 +1,30 @@
-/* 
+/*
  ** Copyright 2007, The Android Open Source Project
  **
- ** Licensed under the Apache License, Version 2.0 (the "License"); 
- ** you may not use this file except in compliance with the License. 
- ** You may obtain a copy of the License at 
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
  **
- **     http://www.apache.org/licenses/LICENSE-2.0 
+ **     http://www.apache.org/licenses/LICENSE-2.0
  **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- ** See the License for the specific language governing permissions and 
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
 
 #include <ctype.h>
-#include <string.h>
 #include <errno.h>
-
+#include <string.h>
 #include <sys/ioctl.h>
 
+#include <log/log.h>
+#include <cutils/properties.h>
+
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
 #include "../hooks.h"
 #include "../egl_impl.h"
 
@@ -90,39 +89,65 @@
 
 #undef API_ENTRY
 #undef CALL_GL_API
+#undef CALL_GL_API_INTERNAL_CALL
+#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+#undef CALL_GL_API_INTERNAL_DO_RETURN
 #undef CALL_GL_API_RETURN
 
 #if USE_SLOW_BINDING
 
     #define API_ENTRY(_api) _api
 
-    #define CALL_GL_API(_api, ...)                                       \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                         \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
         if (_c) return _c->_api(__VA_ARGS__);
 
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE return 0;
+
+    // This stays blank, since void functions will implicitly return, and
+    // all of the other functions will return 0 based on the previous macro.
+    #define CALL_GL_API_INTERNAL_DO_RETURN
+
 #elif defined(__arm__)
 
     #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                              \
-         asm volatile(                                          \
-            GET_TLS(r12)                                        \
-            "ldr   r12, [r12, %[tls]] \n"                       \
-            "cmp   r12, #0            \n"                       \
-            "ldrne pc,  [r12, %[api]] \n"                       \
-            :                                                   \
-            : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
-              [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api))    \
-            : "r12"                                             \
-            );
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                 \
+        asm volatile(                                            \
+            GET_TLS(r12)                                         \
+            "ldr   r12, [r12, %[tls]] \n"                        \
+            "cmp   r12, #0            \n"                        \
+            "ldrne pc,  [r12, %[api]] \n"                        \
+            :                                                    \
+            : [tls] "J"(TLS_SLOT_OPENGL_API*4),                  \
+              [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+            : "r0", "r1", "r2", "r3", "r12"                      \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        asm volatile(                             \
+            "mov r0, #0 \n"                       \
+            :                                     \
+            :                                     \
+            : "r0"                                \
+        );
+
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        asm volatile(                      \
+            "bx lr \n"                     \
+            :                              \
+            :                              \
+            : "r0"                         \
+        );
 
 #elif defined(__aarch64__)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
         asm volatile(                                               \
             "mrs x16, tpidr_el0\n"                                  \
             "ldr x16, [x16, %[tls]]\n"                              \
@@ -133,120 +158,173 @@
             :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)),      \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "x16"                                                 \
+            : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x16" \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        asm volatile(                             \
+            "mov w0, wzr \n"                      \
+            :                                     \
+            :                                     \
+            : "w0"                                \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        asm volatile(                      \
+            "ret \n"                       \
+            :                              \
+            :                              \
+            :                              \
         );
 
 #elif defined(__i386__)
 
-    #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
-        register void* fn;                                          \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
         __asm__ volatile(                                           \
-            "mov %%gs:0, %[fn]\n"                                   \
-            "mov %P[tls](%[fn]), %[fn]\n"                           \
-            "test %[fn], %[fn]\n"                                   \
+            "mov %%gs:0, %%eax\n"                                   \
+            "mov %P[tls](%%eax), %%eax\n"                           \
+            "test %%eax, %%eax\n"                                   \
             "je 1f\n"                                               \
-            "jmp *%P[api](%[fn])\n"                                 \
+            "jmp *%P[api](%%eax)\n"                                 \
             "1:\n"                                                  \
-            : [fn] "=r" (fn)                                        \
+            :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)),        \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "cc"                                                  \
-            );
+            : "cc", "%eax"                                          \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        __asm__ volatile(                         \
+            "xor %%eax, %%eax\n"                  \
+            :                                     \
+            :                                     \
+            : "%eax"                              \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        __asm__ volatile(                  \
+            "ret\n"                        \
+            :                              \
+            :                              \
+            :                              \
+        );
 
 #elif defined(__x86_64__)
 
-    #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                                  \
-         register void** fn;                                        \
-         __asm__ volatile(                                          \
-            "mov %%fs:0, %[fn]\n"                                   \
-            "mov %P[tls](%[fn]), %[fn]\n"                           \
-            "test %[fn], %[fn]\n"                                   \
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                    \
+        __asm__ volatile(                                           \
+            "mov %%fs:0, %%rax\n"                                   \
+            "mov %P[tls](%%rax), %%rax\n"                           \
+            "test %%rax, %%rax\n"                                   \
             "je 1f\n"                                               \
-            "jmp *%P[api](%[fn])\n"                                 \
+            "jmp *%P[api](%%rax)\n"                                 \
             "1:\n"                                                  \
-            : [fn] "=r" (fn)                                        \
+            :                                                       \
             : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)),        \
               [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api))   \
-            : "cc"                                                  \
-            );
+            : "cc", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9",   \
+              "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", \
+              "%xmm6", "%xmm7"                                      \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+        __asm__ volatile(                         \
+            "xor %%eax, %%eax\n"                  \
+            :                                     \
+            :                                     \
+            : "%eax"                              \
+        );
+
+    #define CALL_GL_API_INTERNAL_DO_RETURN \
+        __asm__ volatile(                  \
+            "retq\n"                       \
+            :                              \
+            :                              \
+            :                              \
+        );
 
 #elif defined(__mips64)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                            \
-    register unsigned long _t0 asm("$12");                    \
-    register unsigned long _fn asm("$25");                    \
-    register unsigned long _tls asm("$3");                    \
-    register unsigned long _v0 asm("$2");                     \
-    asm volatile(                                             \
-        ".set  push\n\t"                                      \
-        ".set  noreorder\n\t"                                 \
-        "rdhwr %[tls], $29\n\t"                               \
-        "ld    %[t0], %[OPENGL_API](%[tls])\n\t"              \
-        "beqz  %[t0], 1f\n\t"                                 \
-        " move %[fn], $ra\n\t"                                \
-        "ld    %[t0], %[API](%[t0])\n\t"                      \
-        "beqz  %[t0], 1f\n\t"                                 \
-        " nop\n\t"                                            \
-        "move  %[fn], %[t0]\n\t"                              \
-        "1:\n\t"                                              \
-        "jalr  $0, %[fn]\n\t"                                 \
-        " move %[v0], $0\n\t"                                 \
-        ".set  pop\n\t"                                       \
-        : [fn] "=c"(_fn),                                     \
-          [tls] "=&r"(_tls),                                  \
-          [t0] "=&r"(_t0),                                    \
-          [v0] "=&r"(_v0)                                     \
-        : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
-          [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api))  \
-        :                                                     \
+    // t0:  $12
+    // fn:  $25
+    // tls: $3
+    // v0:  $2
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                  \
+        asm volatile(                                             \
+            ".set  push\n\t"                                      \
+            ".set  noreorder\n\t"                                 \
+            "rdhwr $3, $29\n\t"                                   \
+            "ld    $12, %[OPENGL_API]($3)\n\t"                    \
+            "beqz  $12, 1f\n\t"                                   \
+            " move $25, $ra\n\t"                                  \
+            "ld    $12, %[API]($12)\n\t"                          \
+            "beqz  $12, 1f\n\t"                                   \
+            " nop\n\t"                                            \
+            "move  $25, $12\n\t"                                  \
+            "1:\n\t"                                              \
+            "jalr  $0, $25\n\t"                                   \
+            " move $2, $0\n\t"                                    \
+            ".set  pop\n\t"                                       \
+            :                                                     \
+            : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
+              [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api))  \
+            : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",     \
+              "$10", "$11", "$12", "$25"                          \
         );
 
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+    #define CALL_GL_API_INTERNAL_DO_RETURN
+
 #elif defined(__mips__)
 
-    #define API_ENTRY(_api) __attribute__((noinline)) _api
+    #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
 
-    #define CALL_GL_API(_api, ...)                               \
-        register unsigned int _t0 asm("$8");                     \
-        register unsigned int _fn asm("$25");                    \
-        register unsigned int _tls asm("$3");                    \
-        register unsigned int _v0 asm("$2");                     \
+    // t0:  $8
+    // fn:  $25
+    // tls: $3
+    // v0:  $2
+    #define CALL_GL_API_INTERNAL_CALL(_api, ...)                 \
         asm volatile(                                            \
             ".set  push\n\t"                                     \
             ".set  noreorder\n\t"                                \
             ".set  mips32r2\n\t"                                 \
-            "rdhwr %[tls], $29\n\t"                              \
-            "lw    %[t0], %[OPENGL_API](%[tls])\n\t"             \
-            "beqz  %[t0], 1f\n\t"                                \
-            " move %[fn], $ra\n\t"                               \
-            "lw    %[t0], %[API](%[t0])\n\t"                     \
-            "beqz  %[t0], 1f\n\t"                                \
+            "rdhwr $3, $29\n\t"                                  \
+            "lw    $3, %[OPENGL_API]($3)\n\t"                    \
+            "beqz  $3, 1f\n\t"                                   \
+            " move $25,$ra\n\t"                                  \
+            "lw    $3, %[API]($3)\n\t"                           \
+            "beqz  $3, 1f\n\t"                                   \
             " nop\n\t"                                           \
-            "move  %[fn], %[t0]\n\t"                             \
+            "move  $25, $3\n\t"                                  \
             "1:\n\t"                                             \
-            "jalr  $0, %[fn]\n\t"                                \
-            " move %[v0], $0\n\t"                                \
+            "jalr  $0, $25\n\t"                                  \
+            " move $2, $0\n\t"                                   \
             ".set  pop\n\t"                                      \
-            : [fn] "=c"(_fn),                                    \
-              [tls] "=&r"(_tls),                                 \
-              [t0] "=&r"(_t0),                                   \
-              [v0] "=&r"(_v0)                                    \
+            :                                                    \
             : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4),           \
               [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
-            :                                                    \
-            );
+            : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25"    \
+        );
+
+    #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+    #define CALL_GL_API_INTERNAL_DO_RETURN
 
 #endif
 
-#define CALL_GL_API_RETURN(_api, ...) \
-    CALL_GL_API(_api, __VA_ARGS__) \
-    return 0;
+#define CALL_GL_API(_api, ...) \
+    CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \
+    CALL_GL_API_INTERNAL_DO_RETURN
 
+#define CALL_GL_API_RETURN(_api, ...) \
+    CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \
+    CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+    CALL_GL_API_INTERNAL_DO_RETURN
 
 extern "C" {
 #pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -257,6 +335,9 @@
 
 #undef API_ENTRY
 #undef CALL_GL_API
+#undef CALL_GL_API_INTERNAL_CALL
+#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE
+#undef CALL_GL_API_INTERNAL_DO_RETURN
 #undef CALL_GL_API_RETURN
 
 /*
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index c0990ec..a8855ef 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_EGL_IMPL_H
 #define ANDROID_EGL_IMPL_H
 
-#include <ctype.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <EGL/eglplatform.h>
@@ -30,8 +28,7 @@
 // ----------------------------------------------------------------------------
 
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
-EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name,
-                                                          GLuint index);
+EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
 EGLAPI GLint egl_get_num_extensions_for_current_context();
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index e14075c..81dbe0e 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -56,8 +56,8 @@
 
 #undef GL_ENTRY
 #undef EGL_ENTRY
-#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
-#define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
+#define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
+#define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
 
 struct egl_t {
     #include "EGL/egl_entries.in"
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
new file mode 100644
index 0000000..89269a0
--- /dev/null
+++ b/opengl/libs/libEGL.map.txt
@@ -0,0 +1,68 @@
+LIBEGL {
+  global:
+    eglBindAPI;
+    eglBindTexImage;
+    eglChooseConfig;
+    eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglCopyBuffers;
+    eglCreateContext;
+    eglCreateImageKHR;
+    eglCreateNativeClientBufferANDROID; # introduced=24
+    eglCreatePbufferFromClientBuffer;
+    eglCreatePbufferSurface;
+    eglCreatePixmapSurface;
+    eglCreateStreamFromFileDescriptorKHR; # introduced=23
+    eglCreateStreamKHR; # introduced=23
+    eglCreateStreamProducerSurfaceKHR; # introduced=23
+    eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglCreateWindowSurface;
+    eglDestroyContext;
+    eglDestroyImageKHR;
+    eglDestroyStreamKHR; # introduced=23
+    eglDestroySurface;
+    eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglGetConfigAttrib;
+    eglGetConfigs;
+    eglGetCurrentContext;
+    eglGetCurrentDisplay;
+    eglGetCurrentSurface;
+    eglGetDisplay;
+    eglGetError;
+    eglGetNativeClientBufferANDROID; # introduced=26
+    eglGetProcAddress;
+    eglGetStreamFileDescriptorKHR; # introduced=23
+    eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    eglInitialize;
+    eglLockSurfaceKHR;
+    eglMakeCurrent;
+    eglPresentationTimeANDROID; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglQueryAPI;
+    eglQueryContext;
+    eglQueryStreamKHR; # introduced=23
+    eglQueryStreamTimeKHR; # introduced=23
+    eglQueryStreamu64KHR; # introduced=23
+    eglQueryString;
+    eglQuerySurface;
+    eglReleaseTexImage;
+    eglReleaseThread;
+    eglSetDamageRegionKHR; # introduced=23
+    eglSignalSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglStreamAttribKHR; # introduced=23
+    eglStreamConsumerAcquireKHR; # introduced=23
+    eglStreamConsumerGLTextureExternalKHR; # introduced=23
+    eglStreamConsumerReleaseKHR; # introduced=23
+    eglSurfaceAttrib;
+    eglSwapBuffers;
+    eglSwapBuffersWithDamageKHR; # introduced=23
+    eglSwapInterval;
+    eglTerminate;
+    eglUnlockSurfaceKHR;
+    eglWaitClient;
+    eglWaitGL;
+    eglWaitNative;
+    eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv1_CM.map.txt b/opengl/libs/libGLESv1_CM.map.txt
new file mode 100644
index 0000000..8ba91e6
--- /dev/null
+++ b/opengl/libs/libGLESv1_CM.map.txt
@@ -0,0 +1,283 @@
+LIBGLESV1_CM {
+  global:
+    glActiveTexture;
+    glAlphaFunc;
+    glAlphaFuncx;
+    glAlphaFuncxOES;
+    glBindBuffer;
+    glBindFramebufferOES;
+    glBindRenderbufferOES;
+    glBindTexture;
+    glBindVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glBlendEquationOES;
+    glBlendEquationSeparateOES;
+    glBlendFunc;
+    glBlendFuncSeparateOES;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatusOES;
+    glClear;
+    glClearColor;
+    glClearColorx;
+    glClearColorxOES;
+    glClearDepthf;
+    glClearDepthfOES;
+    glClearDepthx;
+    glClearDepthxOES;
+    glClearStencil;
+    glClientActiveTexture;
+    glClipPlanef;
+    glClipPlanefIMG; # introduced-mips=9 introduced-x86=9
+    glClipPlanefOES;
+    glClipPlanex;
+    glClipPlanexIMG; # introduced-mips=9 introduced-x86=9
+    glClipPlanexOES;
+    glColor4f;
+    glColor4ub;
+    glColor4x;
+    glColor4xOES;
+    glColorMask;
+    glColorPointer;
+    glColorPointerBounds;
+    glCompressedTexImage2D;
+    glCompressedTexSubImage2D;
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCullFace;
+    glCurrentPaletteMatrixOES;
+    glDeleteBuffers;
+    glDeleteFencesNV; # introduced-mips=9 introduced-x86=9
+    glDeleteFramebuffersOES;
+    glDeleteRenderbuffersOES;
+    glDeleteTextures;
+    glDeleteVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDepthRangefOES;
+    glDepthRangex;
+    glDepthRangexOES;
+    glDisable;
+    glDisableClientState;
+    glDisableDriverControlQCOM; # introduced-mips=9 introduced-x86=9
+    glDiscardFramebufferEXT; # introduced-mips=9 introduced-x86=9
+    glDrawArrays;
+    glDrawElements;
+    glDrawTexfOES;
+    glDrawTexfvOES;
+    glDrawTexiOES;
+    glDrawTexivOES;
+    glDrawTexsOES;
+    glDrawTexsvOES;
+    glDrawTexxOES;
+    glDrawTexxvOES;
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableClientState;
+    glEnableDriverControlQCOM; # introduced-mips=9 introduced-x86=9
+    glEndTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBufferPointervQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetFramebuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramBinarySourceQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramsQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetRenderbuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetShadersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexLevelParameterivQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexSubImageQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexturesQCOM; # introduced-mips=9 introduced-x86=9
+    glExtIsProgramBinaryQCOM; # introduced-mips=9 introduced-x86=9
+    glExtTexObjectStateOverrideiQCOM; # introduced-mips=9 introduced-x86=9
+    glFinish;
+    glFinishFenceNV; # introduced-mips=9 introduced-x86=9
+    glFlush;
+    glFogf;
+    glFogfv;
+    glFogx;
+    glFogxOES;
+    glFogxv;
+    glFogxvOES;
+    glFramebufferRenderbufferOES;
+    glFramebufferTexture2DMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glFramebufferTexture2DOES;
+    glFrontFace;
+    glFrustumf;
+    glFrustumfOES;
+    glFrustumx;
+    glFrustumxOES;
+    glGenBuffers;
+    glGenFencesNV; # introduced-mips=9 introduced-x86=9
+    glGenFramebuffersOES;
+    glGenRenderbuffersOES;
+    glGenTextures;
+    glGenVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glGenerateMipmapOES;
+    glGetBooleanv;
+    glGetBufferParameteriv;
+    glGetBufferPointervOES;
+    glGetClipPlanef;
+    glGetClipPlanefOES;
+    glGetClipPlanex;
+    glGetClipPlanexOES;
+    glGetDriverControlStringQCOM; # introduced-mips=9 introduced-x86=9
+    glGetDriverControlsQCOM; # introduced-mips=9 introduced-x86=9
+    glGetError;
+    glGetFenceivNV; # introduced-mips=9 introduced-x86=9
+    glGetFixedv;
+    glGetFixedvOES;
+    glGetFloatv;
+    glGetFramebufferAttachmentParameterivOES;
+    glGetIntegerv;
+    glGetLightfv;
+    glGetLightxv;
+    glGetLightxvOES;
+    glGetMaterialfv;
+    glGetMaterialxv;
+    glGetMaterialxvOES;
+    glGetPointerv;
+    glGetRenderbufferParameterivOES;
+    glGetString;
+    glGetTexEnvfv;
+    glGetTexEnviv;
+    glGetTexEnvxv;
+    glGetTexEnvxvOES;
+    glGetTexGenfvOES;
+    glGetTexGenivOES;
+    glGetTexGenxvOES;
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetTexParameterxv;
+    glGetTexParameterxvOES;
+    glHint;
+    glIsBuffer;
+    glIsEnabled;
+    glIsFenceNV; # introduced-mips=9 introduced-x86=9
+    glIsFramebufferOES;
+    glIsRenderbufferOES;
+    glIsTexture;
+    glIsVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glLightModelf;
+    glLightModelfv;
+    glLightModelx;
+    glLightModelxOES;
+    glLightModelxv;
+    glLightModelxvOES;
+    glLightf;
+    glLightfv;
+    glLightx;
+    glLightxOES;
+    glLightxv;
+    glLightxvOES;
+    glLineWidth;
+    glLineWidthx;
+    glLineWidthxOES;
+    glLoadIdentity;
+    glLoadMatrixf;
+    glLoadMatrixx;
+    glLoadMatrixxOES;
+    glLoadPaletteFromModelViewMatrixOES;
+    glLogicOp;
+    glMapBufferOES;
+    glMaterialf;
+    glMaterialfv;
+    glMaterialx;
+    glMaterialxOES;
+    glMaterialxv;
+    glMaterialxvOES;
+    glMatrixIndexPointerOES;
+    glMatrixIndexPointerOESBounds; # introduced-mips=9 introduced-x86=9
+    glMatrixMode;
+    glMultMatrixf;
+    glMultMatrixx;
+    glMultMatrixxOES;
+    glMultiDrawArraysEXT; # introduced-mips=9 introduced-x86=9
+    glMultiDrawElementsEXT; # introduced-mips=9 introduced-x86=9
+    glMultiTexCoord4f;
+    glMultiTexCoord4x;
+    glMultiTexCoord4xOES;
+    glNormal3f;
+    glNormal3x;
+    glNormal3xOES;
+    glNormalPointer;
+    glNormalPointerBounds;
+    glOrthof;
+    glOrthofOES;
+    glOrthox;
+    glOrthoxOES;
+    glPixelStorei;
+    glPointParameterf;
+    glPointParameterfv;
+    glPointParameterx;
+    glPointParameterxOES;
+    glPointParameterxv;
+    glPointParameterxvOES;
+    glPointSize;
+    glPointSizePointerOES;
+    glPointSizePointerOESBounds; # introduced-mips=9 introduced-x86=9
+    glPointSizex;
+    glPointSizexOES;
+    glPolygonOffset;
+    glPolygonOffsetx;
+    glPolygonOffsetxOES;
+    glPopMatrix;
+    glPushMatrix;
+    glQueryMatrixxOES;
+    glReadPixels;
+    glRenderbufferStorageMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glRenderbufferStorageOES;
+    glRotatef;
+    glRotatex;
+    glRotatexOES;
+    glSampleCoverage;
+    glSampleCoveragex;
+    glSampleCoveragexOES;
+    glScalef;
+    glScalex;
+    glScalexOES;
+    glScissor;
+    glSetFenceNV; # introduced-mips=9 introduced-x86=9
+    glShadeModel;
+    glStartTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glStencilFunc;
+    glStencilMask;
+    glStencilOp;
+    glTestFenceNV; # introduced-mips=9 introduced-x86=9
+    glTexCoordPointer;
+    glTexCoordPointerBounds;
+    glTexEnvf;
+    glTexEnvfv;
+    glTexEnvi;
+    glTexEnviv;
+    glTexEnvx;
+    glTexEnvxOES;
+    glTexEnvxv;
+    glTexEnvxvOES;
+    glTexGenfOES;
+    glTexGenfvOES;
+    glTexGeniOES;
+    glTexGenivOES;
+    glTexGenxOES;
+    glTexGenxvOES;
+    glTexImage2D;
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexParameterx;
+    glTexParameterxOES;
+    glTexParameterxv;
+    glTexParameterxvOES;
+    glTexSubImage2D;
+    glTranslatef;
+    glTranslatex;
+    glTranslatexOES;
+    glUnmapBufferOES;
+    glVertexPointer;
+    glVertexPointerBounds;
+    glViewport;
+    glWeightPointerOES;
+    glWeightPointerOESBounds; # introduced-mips=9 introduced-x86=9
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv2.map.txt b/opengl/libs/libGLESv2.map.txt
new file mode 100644
index 0000000..1b0042a
--- /dev/null
+++ b/opengl/libs/libGLESv2.map.txt
@@ -0,0 +1,207 @@
+LIBGLESV2 {
+  global:
+    glActiveTexture;
+    glAttachShader;
+    glBeginPerfMonitorAMD;
+    glBindAttribLocation;
+    glBindBuffer;
+    glBindFramebuffer;
+    glBindRenderbuffer;
+    glBindTexture;
+    glBindVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glBlendColor;
+    glBlendEquation;
+    glBlendEquationSeparate;
+    glBlendFunc;
+    glBlendFuncSeparate;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatus;
+    glClear;
+    glClearColor;
+    glClearDepthf;
+    glClearStencil;
+    glColorMask;
+    glCompileShader;
+    glCompressedTexImage2D;
+    glCompressedTexImage3DOES;
+    glCompressedTexSubImage2D;
+    glCompressedTexSubImage3DOES;
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCopyTexSubImage3DOES;
+    glCoverageMaskNV; # introduced-mips=9 introduced-x86=9
+    glCoverageOperationNV; # introduced-mips=9 introduced-x86=9
+    glCreateProgram;
+    glCreateShader;
+    glCullFace;
+    glDeleteBuffers;
+    glDeleteFencesNV;
+    glDeleteFramebuffers;
+    glDeletePerfMonitorsAMD;
+    glDeleteProgram;
+    glDeleteRenderbuffers;
+    glDeleteShader;
+    glDeleteTextures;
+    glDeleteVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDetachShader;
+    glDisable;
+    glDisableDriverControlQCOM;
+    glDisableVertexAttribArray;
+    glDiscardFramebufferEXT; # introduced-mips=9 introduced-x86=9
+    glDrawArrays;
+    glDrawElements;
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableDriverControlQCOM;
+    glEnableVertexAttribArray;
+    glEndPerfMonitorAMD;
+    glEndTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBufferPointervQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetFramebuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramBinarySourceQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramsQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetRenderbuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetShadersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexLevelParameterivQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexSubImageQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexturesQCOM; # introduced-mips=9 introduced-x86=9
+    glExtIsProgramBinaryQCOM; # introduced-mips=9 introduced-x86=9
+    glExtTexObjectStateOverrideiQCOM; # introduced-mips=9 introduced-x86=9
+    glFinish;
+    glFinishFenceNV;
+    glFlush;
+    glFramebufferRenderbuffer;
+    glFramebufferTexture2D;
+    glFramebufferTexture2DMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glFramebufferTexture3DOES;
+    glFrontFace;
+    glGenBuffers;
+    glGenFencesNV;
+    glGenFramebuffers;
+    glGenPerfMonitorsAMD;
+    glGenRenderbuffers;
+    glGenTextures;
+    glGenVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glGenerateMipmap;
+    glGetActiveAttrib;
+    glGetActiveUniform;
+    glGetAttachedShaders;
+    glGetAttribLocation;
+    glGetBooleanv;
+    glGetBufferParameteriv;
+    glGetBufferPointervOES;
+    glGetDriverControlStringQCOM;
+    glGetDriverControlsQCOM;
+    glGetError;
+    glGetFenceivNV;
+    glGetFloatv;
+    glGetFramebufferAttachmentParameteriv;
+    glGetIntegerv;
+    glGetPerfMonitorCounterDataAMD;
+    glGetPerfMonitorCounterInfoAMD;
+    glGetPerfMonitorCounterStringAMD;
+    glGetPerfMonitorCountersAMD;
+    glGetPerfMonitorGroupStringAMD;
+    glGetPerfMonitorGroupsAMD;
+    glGetProgramBinaryOES;
+    glGetProgramInfoLog;
+    glGetProgramiv;
+    glGetRenderbufferParameteriv;
+    glGetShaderInfoLog;
+    glGetShaderPrecisionFormat;
+    glGetShaderSource;
+    glGetShaderiv;
+    glGetString;
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetUniformLocation;
+    glGetUniformfv;
+    glGetUniformiv;
+    glGetVertexAttribPointerv;
+    glGetVertexAttribfv;
+    glGetVertexAttribiv;
+    glHint;
+    glIsBuffer;
+    glIsEnabled;
+    glIsFenceNV;
+    glIsFramebuffer;
+    glIsProgram;
+    glIsRenderbuffer;
+    glIsShader;
+    glIsTexture;
+    glIsVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glLineWidth;
+    glLinkProgram;
+    glMapBufferOES;
+    glMultiDrawArraysEXT; # introduced-mips=9 introduced-x86=9
+    glMultiDrawElementsEXT; # introduced-mips=9 introduced-x86=9
+    glPixelStorei;
+    glPolygonOffset;
+    glProgramBinaryOES;
+    glReadPixels;
+    glReleaseShaderCompiler;
+    glRenderbufferStorage;
+    glRenderbufferStorageMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glSampleCoverage;
+    glScissor;
+    glSelectPerfMonitorCountersAMD;
+    glSetFenceNV;
+    glShaderBinary;
+    glShaderSource;
+    glStartTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glStencilFunc;
+    glStencilFuncSeparate;
+    glStencilMask;
+    glStencilMaskSeparate;
+    glStencilOp;
+    glStencilOpSeparate;
+    glTestFenceNV;
+    glTexImage2D;
+    glTexImage3DOES;
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexSubImage2D;
+    glTexSubImage3DOES;
+    glUniform1f;
+    glUniform1fv;
+    glUniform1i;
+    glUniform1iv;
+    glUniform2f;
+    glUniform2fv;
+    glUniform2i;
+    glUniform2iv;
+    glUniform3f;
+    glUniform3fv;
+    glUniform3i;
+    glUniform3iv;
+    glUniform4f;
+    glUniform4fv;
+    glUniform4i;
+    glUniform4iv;
+    glUniformMatrix2fv;
+    glUniformMatrix3fv;
+    glUniformMatrix4fv;
+    glUnmapBufferOES;
+    glUseProgram;
+    glValidateProgram;
+    glVertexAttrib1f;
+    glVertexAttrib1fv;
+    glVertexAttrib2f;
+    glVertexAttrib2fv;
+    glVertexAttrib3f;
+    glVertexAttrib3fv;
+    glVertexAttrib4f;
+    glVertexAttrib4fv;
+    glVertexAttribPointer;
+    glViewport;
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv3.map.txt b/opengl/libs/libGLESv3.map.txt
new file mode 100644
index 0000000..21f6cb6
--- /dev/null
+++ b/opengl/libs/libGLESv3.map.txt
@@ -0,0 +1,416 @@
+LIBGLESV3 {
+  global:
+    glActiveShaderProgram; # introduced=21
+    glActiveTexture;
+    glAttachShader;
+    glBeginQuery;
+    glBeginTransformFeedback;
+    glBindAttribLocation;
+    glBindBuffer;
+    glBindBufferBase;
+    glBindBufferRange;
+    glBindFramebuffer;
+    glBindImageTexture; # introduced=21
+    glBindProgramPipeline; # introduced=21
+    glBindRenderbuffer;
+    glBindSampler;
+    glBindTexture;
+    glBindTransformFeedback;
+    glBindVertexArray;
+    glBindVertexArrayOES;
+    glBindVertexBuffer; # introduced=21
+    glBlendBarrier; # introduced=24
+    glBlendBarrierKHR; # introduced=21
+    glBlendColor;
+    glBlendEquation;
+    glBlendEquationSeparate;
+    glBlendEquationSeparatei; # introduced=24
+    glBlendEquationSeparateiEXT; # introduced=21
+    glBlendEquationi; # introduced=24
+    glBlendEquationiEXT; # introduced=21
+    glBlendFunc;
+    glBlendFuncSeparate;
+    glBlendFuncSeparatei; # introduced=24
+    glBlendFuncSeparateiEXT; # introduced=21
+    glBlendFunci; # introduced=24
+    glBlendFunciEXT; # introduced=21
+    glBlitFramebuffer;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatus;
+    glClear;
+    glClearBufferfi;
+    glClearBufferfv;
+    glClearBufferiv;
+    glClearBufferuiv;
+    glClearColor;
+    glClearDepthf;
+    glClearStencil;
+    glClientWaitSync;
+    glColorMask;
+    glColorMaski; # introduced=24
+    glColorMaskiEXT; # introduced=21
+    glCompileShader;
+    glCompressedTexImage2D;
+    glCompressedTexImage3D;
+    glCompressedTexImage3DOES;
+    glCompressedTexSubImage2D;
+    glCompressedTexSubImage3D;
+    glCompressedTexSubImage3DOES;
+    glCopyBufferSubData;
+    glCopyImageSubData; # introduced=24
+    glCopyImageSubDataEXT; # introduced=21
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCopyTexSubImage3D;
+    glCopyTexSubImage3DOES;
+    glCreateProgram;
+    glCreateShader;
+    glCreateShaderProgramv; # introduced=21
+    glCullFace;
+    glDebugMessageCallback; # introduced=24
+    glDebugMessageCallbackKHR; # introduced=21
+    glDebugMessageControl; # introduced=24
+    glDebugMessageControlKHR; # introduced=21
+    glDebugMessageInsert; # introduced=24
+    glDebugMessageInsertKHR; # introduced=21
+    glDeleteBuffers;
+    glDeleteFramebuffers;
+    glDeleteProgram;
+    glDeleteProgramPipelines; # introduced=21
+    glDeleteQueries;
+    glDeleteRenderbuffers;
+    glDeleteSamplers;
+    glDeleteShader;
+    glDeleteSync;
+    glDeleteTextures;
+    glDeleteTransformFeedbacks;
+    glDeleteVertexArrays;
+    glDeleteVertexArraysOES;
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDetachShader;
+    glDisable;
+    glDisableVertexAttribArray;
+    glDisablei; # introduced=24
+    glDisableiEXT; # introduced=21
+    glDispatchCompute; # introduced=21
+    glDispatchComputeIndirect; # introduced=21
+    glDrawArrays;
+    glDrawArraysIndirect; # introduced=21
+    glDrawArraysInstanced;
+    glDrawBuffers;
+    glDrawElements;
+    glDrawElementsBaseVertex; # introduced=24
+    glDrawElementsIndirect; # introduced=21
+    glDrawElementsInstanced;
+    glDrawElementsInstancedBaseVertex; # introduced=24
+    glDrawRangeElements;
+    glDrawRangeElementsBaseVertex; # introduced=24
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableVertexAttribArray;
+    glEnablei; # introduced=24
+    glEnableiEXT; # introduced=21
+    glEndQuery;
+    glEndTransformFeedback;
+    glFenceSync;
+    glFinish;
+    glFlush;
+    glFlushMappedBufferRange;
+    glFramebufferParameteri; # introduced=21
+    glFramebufferRenderbuffer;
+    glFramebufferTexture; # introduced=24
+    glFramebufferTexture2D;
+    glFramebufferTexture3DOES;
+    glFramebufferTextureEXT; # introduced=21
+    glFramebufferTextureLayer;
+    glFrontFace;
+    glGenBuffers;
+    glGenFramebuffers;
+    glGenProgramPipelines; # introduced=21
+    glGenQueries;
+    glGenRenderbuffers;
+    glGenSamplers;
+    glGenTextures;
+    glGenTransformFeedbacks;
+    glGenVertexArrays;
+    glGenVertexArraysOES;
+    glGenerateMipmap;
+    glGetActiveAttrib;
+    glGetActiveUniform;
+    glGetActiveUniformBlockName;
+    glGetActiveUniformBlockiv;
+    glGetActiveUniformsiv;
+    glGetAttachedShaders;
+    glGetAttribLocation;
+    glGetBooleani_v; # introduced=21
+    glGetBooleanv;
+    glGetBufferParameteri64v;
+    glGetBufferParameteriv;
+    glGetBufferPointerv;
+    glGetBufferPointervOES;
+    glGetDebugMessageLog; # introduced=24
+    glGetDebugMessageLogKHR; # introduced=21
+    glGetError;
+    glGetFloatv;
+    glGetFragDataLocation;
+    glGetFramebufferAttachmentParameteriv;
+    glGetFramebufferParameteriv; # introduced=21
+    glGetGraphicsResetStatus; # introduced=24
+    glGetInteger64i_v;
+    glGetInteger64v;
+    glGetIntegeri_v;
+    glGetIntegerv;
+    glGetInternalformativ;
+    glGetMultisamplefv; # introduced=21
+    glGetObjectLabel; # introduced=24
+    glGetObjectLabelKHR; # introduced=21
+    glGetObjectPtrLabel; # introduced=24
+    glGetObjectPtrLabelKHR; # introduced=21
+    glGetPointerv; # introduced=24
+    glGetPointervKHR; # introduced=21
+    glGetProgramBinary;
+    glGetProgramBinaryOES;
+    glGetProgramInfoLog;
+    glGetProgramInterfaceiv; # introduced=21
+    glGetProgramPipelineInfoLog; # introduced=21
+    glGetProgramPipelineiv; # introduced=21
+    glGetProgramResourceIndex; # introduced=21
+    glGetProgramResourceLocation; # introduced=21
+    glGetProgramResourceName; # introduced=21
+    glGetProgramResourceiv; # introduced=21
+    glGetProgramiv;
+    glGetQueryObjectuiv;
+    glGetQueryiv;
+    glGetRenderbufferParameteriv;
+    glGetSamplerParameterIiv; # introduced=24
+    glGetSamplerParameterIivEXT; # introduced=21
+    glGetSamplerParameterIuiv; # introduced=24
+    glGetSamplerParameterIuivEXT; # introduced=21
+    glGetSamplerParameterfv;
+    glGetSamplerParameteriv;
+    glGetShaderInfoLog;
+    glGetShaderPrecisionFormat;
+    glGetShaderSource;
+    glGetShaderiv;
+    glGetString;
+    glGetStringi;
+    glGetSynciv;
+    glGetTexLevelParameterfv; # introduced=21
+    glGetTexLevelParameteriv; # introduced=21
+    glGetTexParameterIiv; # introduced=24
+    glGetTexParameterIivEXT; # introduced=21
+    glGetTexParameterIuiv; # introduced=24
+    glGetTexParameterIuivEXT; # introduced=21
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetTransformFeedbackVarying;
+    glGetUniformBlockIndex;
+    glGetUniformIndices;
+    glGetUniformLocation;
+    glGetUniformfv;
+    glGetUniformiv;
+    glGetUniformuiv;
+    glGetVertexAttribIiv;
+    glGetVertexAttribIuiv;
+    glGetVertexAttribPointerv;
+    glGetVertexAttribfv;
+    glGetVertexAttribiv;
+    glGetnUniformfv; # introduced=24
+    glGetnUniformiv; # introduced=24
+    glGetnUniformuiv; # introduced=24
+    glHint;
+    glInvalidateFramebuffer;
+    glInvalidateSubFramebuffer;
+    glIsBuffer;
+    glIsEnabled;
+    glIsEnabledi; # introduced=24
+    glIsEnablediEXT; # introduced=21
+    glIsFramebuffer;
+    glIsProgram;
+    glIsProgramPipeline; # introduced=21
+    glIsQuery;
+    glIsRenderbuffer;
+    glIsSampler;
+    glIsShader;
+    glIsSync;
+    glIsTexture;
+    glIsTransformFeedback;
+    glIsVertexArray;
+    glIsVertexArrayOES;
+    glLineWidth;
+    glLinkProgram;
+    glMapBufferOES;
+    glMapBufferRange;
+    glMemoryBarrier; # introduced=21
+    glMemoryBarrierByRegion; # introduced=21
+    glMinSampleShading; # introduced=24
+    glMinSampleShadingOES; # introduced=21
+    glObjectLabel; # introduced=24
+    glObjectLabelKHR; # introduced=21
+    glObjectPtrLabel; # introduced=24
+    glObjectPtrLabelKHR; # introduced=21
+    glPatchParameteri; # introduced=24
+    glPatchParameteriEXT; # introduced=21
+    glPauseTransformFeedback;
+    glPixelStorei;
+    glPolygonOffset;
+    glPopDebugGroup; # introduced=24
+    glPopDebugGroupKHR; # introduced=21
+    glPrimitiveBoundingBox; # introduced=24
+    glPrimitiveBoundingBoxEXT; # introduced=21
+    glProgramBinary;
+    glProgramBinaryOES;
+    glProgramParameteri;
+    glProgramUniform1f; # introduced=21
+    glProgramUniform1fv; # introduced=21
+    glProgramUniform1i; # introduced=21
+    glProgramUniform1iv; # introduced=21
+    glProgramUniform1ui; # introduced=21
+    glProgramUniform1uiv; # introduced=21
+    glProgramUniform2f; # introduced=21
+    glProgramUniform2fv; # introduced=21
+    glProgramUniform2i; # introduced=21
+    glProgramUniform2iv; # introduced=21
+    glProgramUniform2ui; # introduced=21
+    glProgramUniform2uiv; # introduced=21
+    glProgramUniform3f; # introduced=21
+    glProgramUniform3fv; # introduced=21
+    glProgramUniform3i; # introduced=21
+    glProgramUniform3iv; # introduced=21
+    glProgramUniform3ui; # introduced=21
+    glProgramUniform3uiv; # introduced=21
+    glProgramUniform4f; # introduced=21
+    glProgramUniform4fv; # introduced=21
+    glProgramUniform4i; # introduced=21
+    glProgramUniform4iv; # introduced=21
+    glProgramUniform4ui; # introduced=21
+    glProgramUniform4uiv; # introduced=21
+    glProgramUniformMatrix2fv; # introduced=21
+    glProgramUniformMatrix2x3fv; # introduced=21
+    glProgramUniformMatrix2x4fv; # introduced=21
+    glProgramUniformMatrix3fv; # introduced=21
+    glProgramUniformMatrix3x2fv; # introduced=21
+    glProgramUniformMatrix3x4fv; # introduced=21
+    glProgramUniformMatrix4fv; # introduced=21
+    glProgramUniformMatrix4x2fv; # introduced=21
+    glProgramUniformMatrix4x3fv; # introduced=21
+    glPushDebugGroup; # introduced=24
+    glPushDebugGroupKHR; # introduced=21
+    glReadBuffer;
+    glReadPixels;
+    glReadnPixels; # introduced=24
+    glReleaseShaderCompiler;
+    glRenderbufferStorage;
+    glRenderbufferStorageMultisample;
+    glResumeTransformFeedback;
+    glSampleCoverage;
+    glSampleMaski; # introduced=21
+    glSamplerParameterIiv; # introduced=24
+    glSamplerParameterIivEXT; # introduced=21
+    glSamplerParameterIuiv; # introduced=24
+    glSamplerParameterIuivEXT; # introduced=21
+    glSamplerParameterf;
+    glSamplerParameterfv;
+    glSamplerParameteri;
+    glSamplerParameteriv;
+    glScissor;
+    glShaderBinary;
+    glShaderSource;
+    glStencilFunc;
+    glStencilFuncSeparate;
+    glStencilMask;
+    glStencilMaskSeparate;
+    glStencilOp;
+    glStencilOpSeparate;
+    glTexBuffer; # introduced=24
+    glTexBufferEXT; # introduced=21
+    glTexBufferRange; # introduced=24
+    glTexBufferRangeEXT; # introduced=21
+    glTexImage2D;
+    glTexImage3D;
+    glTexImage3DOES;
+    glTexParameterIiv; # introduced=24
+    glTexParameterIivEXT; # introduced=21
+    glTexParameterIuiv; # introduced=24
+    glTexParameterIuivEXT; # introduced=21
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexStorage2D;
+    glTexStorage2DMultisample; # introduced=21
+    glTexStorage3D;
+    glTexStorage3DMultisample; # introduced=24
+    glTexStorage3DMultisampleOES; # introduced=21
+    glTexSubImage2D;
+    glTexSubImage3D;
+    glTexSubImage3DOES;
+    glTransformFeedbackVaryings;
+    glUniform1f;
+    glUniform1fv;
+    glUniform1i;
+    glUniform1iv;
+    glUniform1ui;
+    glUniform1uiv;
+    glUniform2f;
+    glUniform2fv;
+    glUniform2i;
+    glUniform2iv;
+    glUniform2ui;
+    glUniform2uiv;
+    glUniform3f;
+    glUniform3fv;
+    glUniform3i;
+    glUniform3iv;
+    glUniform3ui;
+    glUniform3uiv;
+    glUniform4f;
+    glUniform4fv;
+    glUniform4i;
+    glUniform4iv;
+    glUniform4ui;
+    glUniform4uiv;
+    glUniformBlockBinding;
+    glUniformMatrix2fv;
+    glUniformMatrix2x3fv;
+    glUniformMatrix2x4fv;
+    glUniformMatrix3fv;
+    glUniformMatrix3x2fv;
+    glUniformMatrix3x4fv;
+    glUniformMatrix4fv;
+    glUniformMatrix4x2fv;
+    glUniformMatrix4x3fv;
+    glUnmapBuffer;
+    glUnmapBufferOES;
+    glUseProgram;
+    glUseProgramStages; # introduced=21
+    glValidateProgram;
+    glValidateProgramPipeline; # introduced=21
+    glVertexAttrib1f;
+    glVertexAttrib1fv;
+    glVertexAttrib2f;
+    glVertexAttrib2fv;
+    glVertexAttrib3f;
+    glVertexAttrib3fv;
+    glVertexAttrib4f;
+    glVertexAttrib4fv;
+    glVertexAttribBinding; # introduced=21
+    glVertexAttribDivisor;
+    glVertexAttribFormat; # introduced=21
+    glVertexAttribI4i;
+    glVertexAttribI4iv;
+    glVertexAttribI4ui;
+    glVertexAttribI4uiv;
+    glVertexAttribIFormat; # introduced=21
+    glVertexAttribIPointer;
+    glVertexAttribPointer;
+    glVertexBindingDivisor; # introduced=21
+    glViewport;
+    glWaitSync;
+  local:
+    *;
+};
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
deleted file mode 100644
index 51c6c61..0000000
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ /dev/null
@@ -1,190 +0,0 @@
-Name
-
-    ANDROID_create_native_client_buffer
-
-Name Strings
-
-    EGL_ANDROID_create_native_client_buffer
-
-Contributors
-
-    Craig Donner
-
-Contact
-
-    Craig Donner, Google Inc. (cdonner 'at' google.com)
-
-Status
-
-    Draft
-
-Version
-
-    Version 1, January 19, 2016
-
-Number
-
-    EGL Extension #XXX
-
-Dependencies
-
-    Requires EGL 1.2.
-
-    EGL_ANDROID_image_native_buffer and EGL_KHR_image_base are required.
-
-    This extension is written against the wording of the EGL 1.2
-    Specification as modified by EGL_KHR_image_base and
-    EGL_ANDROID_image_native_buffer.
-
-Overview
-
-    This extension allows creating an EGLClientBuffer backed by an Android
-    window buffer (struct ANativeWindowBuffer) which can be later used to
-    create an EGLImage.
-
-New Types
-
-    None.
-
-New Procedures and Functions
-
-EGLClientBuffer eglCreateNativeClientBufferANDROID(
-                        const EGLint *attrib_list)
-
-New Tokens
-
-    EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
-    EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
-    EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
-    EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID 0x00000004
-
-Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
-
-    Add the following to section 2.5.1 "EGLImage Specification" (as modified by
-    the EGL_KHR_image_base and EGL_ANDROID_image_native_buffer specifications),
-    below the description of eglCreateImageKHR:
-
-   "The command
-
-        EGLClientBuffer eglCreateNativeClientBufferANDROID(
-                                const EGLint *attrib_list)
-
-    may be used to create an EGLClientBuffer backed by an ANativeWindowBuffer
-    struct. EGL implementations must guarantee that the lifetime of the
-    returned EGLClientBuffer is at least as long as the EGLImage(s) it is bound
-    to, following the lifetime semantics described below in section 2.5.2; the
-    EGLClientBuffer must be destroyed no earlier than when all of its associated
-    EGLImages are destroyed by eglDestroyImageKHR. <attrib_list> is a list of
-    attribute-value pairs which is used to specify the dimensions, format, and
-    usage of the underlying buffer structure. If <attrib_list> is non-NULL, the
-    last attribute specified in the list must be EGL_NONE.
-
-    Attribute names accepted in <attrib_list> are shown in Table aaa,
-    together with the <target> for which each attribute name is valid, and
-    the default value used for each attribute if it is not included in
-    <attrib_list>.
-
-      +---------------------------------+----------------------+---------------+
-      | Attribute                       | Description          | Default Value |
-      |                                 |                      |               |
-      +---------------------------------+----------------------+---------------+
-      | EGL_NONE                        | Marks the end of the | N/A           |
-      |                                 | attribute-value list |               |
-      | EGL_WIDTH                       | The width of the     | 0             |
-      |                                 | buffer data          |               |
-      | EGL_HEIGHT                      | The height of the    | 0             |
-      |                                 | buffer data          |               |
-      | EGL_RED_SIZE                    | The bits of Red in   | 0             |
-      |                                 | the color buffer     |               |
-      | EGL_GREEN_SIZE                  | The bits of Green in | 0             |
-      |                                 | the color buffer     |               |
-      | EGL_BLUE_SIZE                   | The bits of Blue in  | 0             |
-      |                                 | the color buffer     |               |
-      | EGL_ALPHA_SIZE                  | The bits of Alpha in | 0             |
-      |                                 | the color buffer     |               |
-      |                                 | buffer data          |               |
-      | EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of    | 0             |
-      |                                 | the buffer data      |               |
-      +---------------------------------+----------------------+---------------+
-       Table aaa.  Legal attributes for eglCreateNativeClientBufferANDROID
-       <attrib_list> parameter.
-
-    The maximum width and height may depend on the amount of available memory,
-    which may also depend on the format and usage flags. The values of
-    EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE must be non-zero and
-    correspond to a valid pixel format for the implementation. If EGL_ALPHA_SIZE
-    is non-zero then the combination of all four sizes must correspond to a
-    valid pixel format for the implementation. The
-    EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of the following bits:
-
-        EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: Indicates that the
-        created buffer must have a hardware-protected path to external display
-        sink. If a hardware-protected path is not available, then either don't
-        composite only this buffer (preferred) to the external sink, or (less
-        desirable) do not route the entire composition to the external sink.
-
-        EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: The buffer will be
-        used to create a color-renderable texture.
-
-        EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: The buffer will be used to
-        create a filterable texture.
-
-    Errors
-
-        If eglCreateNativeClientBufferANDROID fails, NULL will be returned, no
-        memory will be allocated, and one of the following errors will be
-        generated:
-
-       * If the value of EGL_WIDTH or EGL_HEIGHT is not positive, the error
-         EGL_BAD_PARAMETER is generated.
-
-       * If the combination of the values of EGL_RED_SIZE, EGL_GREEN_SIZE,
-         EGL_BLUE_SIZE, and EGL_ALPHA_SIZE is not a valid pixel format for the
-         EGL implementation, the error EGL_BAD_PARAMETER is generated.
-
-       * If the value of EGL_NATIVE_BUFFER_ANDROID is not a valid combination
-         of gralloc usage flags for the EGL implementation, or is incompatible
-         with the value of EGL_FORMAT, the error EGL_BAD_PARAMETER is
-         Generated.
-
-Issues
-
-    1. Should this extension define what combinations of formats and usage flags
-    EGL implementations are required to support?
-
-    RESOLVED: Partially.
-
-    The set of valid color combinations is implementation-specific and may
-    depend on additional EGL extensions, but generally RGB565 and RGBA888 should
-    be supported. The particular valid combinations for a given Android version
-    and implementation should be documented by that version.
-
-    2. Should there be an eglDestroyNativeClientBufferANDROID to destroy the
-    client buffers created by this extension?
-
-    RESOLVED: No.
-
-    A destroy function would add several complications:
-
-        a) ANativeWindowBuffer is a reference counted object, may be used
-           outside of EGL.
-        b) The same buffer may back multiple EGLImages, though this usage may
-           result in undefined behavior.
-        c) The interactions between the lifetimes of EGLImages and their
-           EGLClientBuffers would become needlessly complex.
-
-    Because ANativeWindowBuffer is a reference counted object, implementations
-    of this extension should ensure the buffer has a lifetime at least as long
-    as a generated EGLImage (via EGL_ANDROID_image_native_buffer). The simplest
-    method is to increment the reference count of the buffer in
-    eglCreateImagKHR, and then decrement it in eglDestroyImageKHR. This should
-    ensure proper lifetime semantics.
-
-Revision History
-
-#2 (Craig Donner, April 15, 2016)
-    - Set color formats and usage bits explicitly using additional attributes,
-    and add value for new token EGL_NATIVE_BUFFER_USAGE_ANDROID.
-
-#1 (Craig Donner, January 19, 2016)
-    - Initial draft.
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 30337ad..b8a9add 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -8,11 +8,19 @@
 
 Contributors
 
+    Brian Anderson
+    Dan Stoza
     Pablo Ceballos
+    Jesse Hall
+    Fabien Sanglard
 
 Contact
 
+    Brian Anderson, Google Inc. (brianderson 'at' google.com)
+    Dan Stoza, Google Inc. (stoza 'at' google.com)
     Pablo Ceballos, Google Inc. (pceballos 'at' google.com)
+    Jesse Hall, Google Inc. (jessehall 'at' google.com)
+    Fabien Sanglard, Google Inc. (sanglardf 'at' google.com)
 
 Status
 
@@ -20,7 +28,7 @@
 
 Version
 
-    Version 1, May 31, 2016
+    Version 8, April 11, 2017
 
 Number
 
@@ -38,8 +46,8 @@
     and display of window surfaces.
 
     Some examples of how this might be used:
-        - The display retire time can be used to calculate end-to-end latency of
-          the entire graphics pipeline.
+        - The display present time can be used to calculate end-to-end latency
+          of the entire graphics pipeline.
         - The queue time and rendering complete time can be used to determine
           how long the application's rendering took to complete. Likewise, the
           composition start time and finish time can be used to determine how
@@ -57,22 +65,37 @@
 
 New Procedures and Functions
 
-    EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-            EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
-            EGLnsecsANDROID *values);
+    EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId);
 
-    EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint timestamp);
+    EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint numTimestamps,
+            const EGLint *names, EGLnsecsANDROID *values);
+
+    EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR frameId, EGLint numTimestamps,
+            const EGLint *timestamps, EGLnsecsANDROID *values);
+
+    EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint timestamp);
 
 New Tokens
 
-    EGL_TIMESTAMPS_ANDROID 0x314D
-    EGL_QUEUE_TIME_ANDROID 0x314E
-    EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-    EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-    EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-    EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-    EGL_READS_DONE_TIME_ANDROID 0x3433
+    EGL_TIMESTAMPS_ANDROID 0x3430
+    EGL_COMPOSITE_DEADLINE_ANDROID 0x3431
+    EGL_COMPOSITE_INTERVAL_ANDROID 0x3432
+    EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3433
+    EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3434
+    EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3435
+    EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3436
+    EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3437
+    EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3438
+    EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3439
+    EGL_DISPLAY_PRESENT_TIME_ANDROID 0x343A
+    EGL_DEQUEUE_READY_TIME_ANDROID 0x343B
+    EGL_READS_DONE_TIME_ANDROID 0x343C
+    EGL_TIMESTAMP_PENDING_ANDROID -2
+    EGL_TIMESTAMP_INVALID_ANDROID -1
 
 Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
 "Surface Attributes", page 43:
@@ -82,7 +105,6 @@
     enables timestamp collection, while a value of EGL_FALSE disables it. The
     initial value is false. If surface is not a window surface this has no
     effect.
-
 Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors)
 
     Add a new subsection under Section 3,
@@ -91,49 +113,112 @@
 
     The function
 
-        EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint framesAgo, EGLint numTimestamps,
+        EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId);
+
+    Returns an identifier for the next frame to be swapped. The identifier can
+    be used to correlate a particular eglSwapBuffers with its timestamps in
+    eglGetFrameTimestampsANDROID. If any error is generated, the function will
+    return EGL_FALSE.
+
+    The function
+
+        EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy,
+                EGLSurface surface, EGLint numTimestamps,
+                const EGLint *names, EGLnsecsANDROID *values);
+
+    allows querying anticipated timestamps and durations related to the
+    composition and display of a window surface. The values are not associated
+    with a particular frame and can be retrieved before the first swap.
+
+    The eglGetCompositorTimingANDROID function takes an array of names to
+    query and returns their values in the corresponding indices of the values
+    array. The possible names that can be queried are:
+        - EGL_COMPOSITE_DEADLINE_ANDROID - The timestamp of the next time the
+          compositor will begin composition. This is effectively the deadline
+          for when the compositor must receive a newly queued frame.
+        - EGL_COMPOSITE_INTERVAL_ANDROID - The time delta between subsequent
+          composition events.
+        - EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID - The time delta between
+          the start of composition and the expected present time of that
+          composition. This can be used to estimate the latency of the
+          actual present time.
+
+    The function
+
+        EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps,
             const EGLint *timestamps, EGLnsecsANDROID *values);
 
-    allows querying various timestamps related to the composition and display of
-    a window surface.
+    allows querying various timestamps related to the composition and display
+    of specific frames of a window surface.
 
-    The framesAgo parameter indicates how many frames before the last posted
-    frame to query. So a value of zero would indicate that the query is for the
-    last posted frame. Note that the implementation maintains a limited history
-    of timestamp data. If a query is made for a frame whose timestamp history
-    no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection
-    has not been enabled for the surface then EGL_BAD_SURFACE is generated.
-    Timestamps for events that will not occur or have not yet occurred will be
-    zero. Timestamp queries that are not supported will generate an
-    EGL_BAD_PARAMETER error. If any error is generated the function will return
-    EGL_FALSE.
+    The frameId indicates which frame to query. The implementation maintains a
+    limited history of timestamp data. If a query is made for a frame whose
+    timestamp history no longer exists then EGL_BAD_ACCESS is generated. If
+    timestamp collection has not been enabled for the surface then
+    EGL_BAD_SURFACE is generated.  Timestamps for events that might still occur
+    will have the value EGL_TIMESTAMP_PENDING_ANDROID. Timestamps for events
+    that did not occur will have the value EGL_TIMESTAMP_INVALID_ANDROID.
+    Otherwise, the timestamp will be valid and indicate the event has occured.
+    Timestamp queries that are not supported will generate an EGL_BAD_PARAMETER
+    error. If any error is generated the function will return EGL_FALSE.
+
+    The application can poll for the timestamp of particular events by calling
+    eglGetFrameTimestamps over and over without needing to call any other EGL
+    function between calls. This is true even for the most recently swapped
+    frame. eglGetFrameTimestamps is thread safe and can be called from a
+    different thread than the swapping thread.
 
     The eglGetFrameTimestampsANDROID function takes an array of timestamps to
     query and returns timestamps in the corresponding indices of the values
     array. The possible timestamps that can be queried are:
-        - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the
-          application.
+        - EGL_REQUESTED_PRESENT_TIME_ANDROID - The time the application
+          requested this frame be presented. See EGL_ANDROID_presentation_time.
+          If the application does not request a presentation time explicitly,
+          this will correspond to buffer's queue time.
         - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
           application's rendering to the surface was completed.
-        - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
-          began preparing composition for this frame.
-        - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
-          compositor's rendering work for this frame finished. This will be zero
-          if composition was handled by the display and the compositor didn't do
-          any rendering.
-        - EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
-          replaced by the next frame on-screen.
+        - EGL_COMPOSITION_LATCH_TIME_ANDROID - The time when the compositor
+          selected this frame as the one to use for the next composition. The
+          latch is the earliest indication that the frame was submitted in time
+          to be composited.
+        - EGL_FIRST_COMPOSITION_START_TIME_ANDROID - The first time at which
+          the compositor began preparing composition for this frame.
+        - EGL_LAST_COMPOSITION_START_TIME_ANDROID - The last time at which the
+          compositor began preparing composition for this frame. If this frame
+          is composited only once, it will have the same value as
+          EGL_FIRST_COMPOSITION_START_TIME_ANDROID. If the value is not equal,
+          that indicates the subsequent frame was not submitted in time to be
+          latched by the compositor. Note: The value may not be updated for
+          every display refresh if the compositor becomes idle.
+        - EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID - The time at which
+          the compositor's rendering work for this frame finished. This will be
+          zero if composition was handled by the display and the compositor
+          didn't do any rendering.
+        - EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
+          started to scan out to the physical display.
+        - EGL_DEQUEUE_READY_TIME_ANDROID - The time when the buffer became
+          available for reuse as a buffer the client can target without
+          blocking. This is generally the point when all read commands of the
+          buffer have been submitted, but not necessarily completed.
         - EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
           purpose of display/composition were completed for this frame.
 
-    Not all implementations may support all off the above timestamp queries. The
-    function
+    Not all implementations may support all of the above timestamp queries. The
+    functions
 
-        EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint timestamp);
+        EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint name);
 
-    allows querying which timestamps are supported on the implementation."
+    and
+
+        EGLBoolean eglGetFrameTimestampsSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint timestamp);
+
+    allows querying which values are supported by the implementations of
+    eglGetCompositoTimingANDROID and eglGetFrameTimestampsSupportedANDROID
+    respectively."
 
 Issues
 
@@ -141,5 +226,30 @@
 
 Revision History
 
+#8 (Brian Anderson, April 11, 2017)
+    - Use reserved enumerant values.
+
+#7 (Brian Anderson, March 21, 2017)
+    - Differentiate between pending events and events that did not occur.
+
+#6 (Brian Anderson, March 16, 2017)
+    - Remove DISPLAY_RETIRE_TIME_ANDROID.
+
+#5 (Brian Anderson, January 13, 2017)
+    - Add eglGetCompositorTimingANDROID.
+
+#4 (Brian Anderson, January 10, 2017)
+    - Use an absolute frameId rather than a relative framesAgo.
+
+#3 (Brian Anderson, November 30, 2016)
+    - Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
+      EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
+      EGL_DEQUEUE_READY_TIME_ANDROID.
+
+#2 (Brian Anderson, July 22, 2016)
+    - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
+    - Add DISPLAY_PRESENT_TIME_ANDROID.
+
 #1 (Pablo Ceballos, May 31, 2016)
     - Initial draft.
+
diff --git a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
new file mode 100644
index 0000000..772b21a
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
@@ -0,0 +1,99 @@
+Name
+
+    ANDROID_get_native_client_buffer
+
+Name Strings
+
+    EGL_ANDROID_get_native_client_buffer
+
+Contributors
+
+    Craig Donner
+
+Contact
+
+    Craig Donner, Google Inc. (cdonner 'at' google.com)
+
+Status
+
+    Draft
+
+Version
+
+    Version 1.0, January 27, 2017
+
+Number
+
+    EGL Extension #XXX
+
+Dependencies
+
+    Requires EGL 1.2.
+
+    EGL_ANDROID_image_native_buffer and EGL_KHR_image_base are required.
+
+    This extension is written against the wording of the EGL 1.2
+    Specification as modified by EGL_KHR_image_base and
+    EGL_ANDROID_image_native_buffer.
+
+Overview
+
+    This extension allows creating an EGLClientBuffer from an Android
+    AHardwareBuffer object which can be later used to create an EGLImage.
+
+New Types
+
+struct AHardwareBuffer
+
+New Procedures and Functions
+
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer)
+
+New Tokens
+
+    None
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+    Add the following to section 2.5.1 "EGLImage Specification" (as modified by
+    the EGL_KHR_image_base and EGL_ANDROID_image_native_buffer specifications),
+    below the description of eglCreateImageKHR:
+
+   "The command
+
+        EGLClientBuffer eglGetNativeClientBufferANDROID(
+                                AHardwareBuffer *buffer)
+
+    may be used to create an EGLClientBuffer from an AHardwareBuffer object.
+    EGL implementations must guarantee that the lifetime of the returned
+    EGLClientBuffer is at least as long as the EGLImage(s) it is bound to,
+    following the lifetime semantics described below in section 2.5.2; the
+    EGLClientBuffer must be destroyed no earlier than when all of its associated
+    EGLImages are destroyed by eglDestroyImageKHR.
+
+    Errors
+
+        If eglGetNativeClientBufferANDROID fails, NULL will be returned, no
+        memory will be allocated, and the following error will be generated:
+
+       * If the value of buffer is NULL, the error EGL_BAD_PARAMETER is
+         generated.
+
+Issues
+
+    1. Should this extension define what particular AHardwareBuffer formats EGL
+    implementations are required to support?
+
+    RESOLVED: No.
+
+    The set of valid formats is implementation-specific and may depend on
+    additional EGL extensions. The particular valid combinations for a given
+    Android version and implementation should be documented by that version.
+
+Revision History
+
+#2 (Craig Donner, February 17, 2017)
+    - Fix typographical errors.
+
+#1 (Craig Donner, January 27, 2017)
+    - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..cba4453 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -1,15 +1,20 @@
 This directory contains OpenGL ES and EGL extension specifications that have
-been or are being defined for Android.  
+been or are being defined for Android.
 
 The table below tracks usage of EGL enumerant values that have been reserved
 for use by Android extensions.
 
+See https://github.com/KhronosGroup/EGL-Registry/blob/master/api/egl.xml
+for a list of all enumarant values currently reserved and registered with
+Khronos.
+
      Value                       Extension
-----------------     ----------------------------------
+================     ==================================
+0x3140 - 0x314F      Reserved block
+================     ==================================
 0x3140               EGL_NATIVE_BUFFER_ANDROID (EGL_ANDROID_image_native_buffer)
 0x3141               EGL_PLATFORM_ANDROID_KHR (KHR_platform_android)
 0x3142               EGL_RECORDABLE_ANDROID (EGL_ANDROID_recordable)
-0x3143               EGL_NATIVE_BUFFER_USAGE_ANDROID (EGL_ANDROID_create_native_client_buffer)
 0x3144               EGL_SYNC_NATIVE_FENCE_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3145               EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3146               EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
@@ -19,11 +24,23 @@
 0x314A               EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
 0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
 0x314C               EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
-0x314D               EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x314E               EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x314F               EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3430               EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3431               EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3432               EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3433               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3434 - 0x343F      (unused)
+0x314D - 0x314F      (unused)
+
+     Value                       Extension
+================     ==================================
+0x3430 - 0x343F      Reserved block
+================     ==================================
+0x3430               EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3431               EGL_COMPOSITE_DEADLINE_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3432               EGL_COMPOSITE_INTERVAL_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3433               EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3434               EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3435               EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3436               EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3437               EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3438               EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3439               EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x343A               EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x343B               EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x343C               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x343D - 0x343F      (unused)
diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk
index 80e4867..5620496 100644
--- a/opengl/tests/EGLTest/Android.mk
+++ b/opengl/tests/EGLTest/Android.mk
@@ -12,11 +12,17 @@
     EGL_test.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
+	android.hardware.configstore@1.0 \
+	android.hardware.configstore-utils \
 	libEGL \
 	libcutils \
 	libbinder \
+	libhidlbase \
+	libhidltransport \
 	libutils \
 	libgui \
+	libbase \
+	liblog \
 
 LOCAL_C_INCLUDES := \
     bionic/libc/private \
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index d69a275..24b1315 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -16,14 +16,44 @@
 
 #include <gtest/gtest.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+
+#include <configstore/Utils.h>
 #include <utils/String8.h>
 
 #include <EGL/egl.h>
 #include <gui/Surface.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/BufferQueue.h>
 
+#define PIXEL_FORMAT_FLOAT "EGL_EXT_pixel_format_float"
+
+bool hasEglPixelFormatFloat() {
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+    size_t cropExtLen = strlen(PIXEL_FORMAT_FLOAT);
+    size_t extsLen = strlen(exts);
+    bool equal = !strcmp(PIXEL_FORMAT_FLOAT, exts);
+    bool atStart = !strncmp(PIXEL_FORMAT_FLOAT " ", exts, cropExtLen + 1);
+    bool atEnd = (cropExtLen + 1) < extsLen &&
+            !strcmp(" " PIXEL_FORMAT_FLOAT, exts + extsLen - (cropExtLen + 1));
+    bool inMiddle = strstr(exts, " " PIXEL_FORMAT_FLOAT " ");
+    return equal || atStart || atEnd || inMiddle;
+}
 
 namespace android {
 
+#define EGL_UNSIGNED_TRUE static_cast<EGLBoolean>(EGL_TRUE)
+
+// retrieve wide-color setting from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool hasWideColorDisplay =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
 class EGLTest : public ::testing::Test {
 protected:
     EGLDisplay mEglDisplay;
@@ -48,7 +78,7 @@
 
     virtual void TearDown() {
         EGLBoolean success = eglTerminate(mEglDisplay);
-        ASSERT_EQ(EGL_TRUE, success);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
     }
 };
@@ -65,20 +95,20 @@
     };
 
     success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     ASSERT_GE(numConfigs, 1);
 
     EGLint components[3];
 
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
     EXPECT_GE(components[0], 8);
@@ -101,9 +131,9 @@
     EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
 
     struct DummyConsumer : public BnConsumerListener {
-        virtual void onFrameAvailable(const BufferItem& /* item */) {}
-        virtual void onBuffersReleased() {}
-        virtual void onSidebandStreamChanged() {}
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
     };
 
     // Create a EGLSurface
@@ -139,23 +169,23 @@
     };
 
     success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     ASSERT_GE(numConfigs, 1);
 
     EGLint components[4];
 
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
     EXPECT_GE(components[0], 8);
@@ -164,5 +194,75 @@
     EXPECT_GE(components[3], 8);
 }
 
+TEST_F(EGLTest, EGLConfigFP16) {
+    EGLint numConfigs;
+    EGLConfig config;
+    EGLBoolean success;
 
+    if (!hasWideColorDisplay) {
+        // skip this test if device does not have wide-color display
+        return;
+    }
+
+    ASSERT_TRUE(hasEglPixelFormatFloat());
+
+    EGLint attrs[] = {EGL_SURFACE_TYPE,
+                      EGL_WINDOW_BIT,
+                      EGL_RENDERABLE_TYPE,
+                      EGL_OPENGL_ES2_BIT,
+                      EGL_RED_SIZE,
+                      16,
+                      EGL_GREEN_SIZE,
+                      16,
+                      EGL_BLUE_SIZE,
+                      16,
+                      EGL_ALPHA_SIZE,
+                      16,
+                      EGL_COLOR_COMPONENT_TYPE_EXT,
+                      EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
+                      EGL_NONE};
+    success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(1, numConfigs);
+
+    EGLint components[4];
+
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EXPECT_GE(components[0], 16);
+    EXPECT_GE(components[1], 16);
+    EXPECT_GE(components[2], 16);
+    EXPECT_GE(components[3], 16);
+
+    struct DummyConsumer : public BnConsumerListener {
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
+    };
+
+    // Create a EGLSurface
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->consumerConnect(new DummyConsumer, false);
+    sp<Surface> mSTC = new Surface(producer);
+    sp<ANativeWindow> mANW = mSTC;
+
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), NULL);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
 }
diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp
index c5bf296..c974f63 100644
--- a/opengl/tests/EGLTest/egl_cache_test.cpp
+++ b/opengl/tests/EGLTest/egl_cache_test.cpp
@@ -21,9 +21,13 @@
 
 #include <utils/Log.h>
 
+#include <android-base/test_utils.h>
+
 #include "egl_cache.h"
 #include "egl_display.h"
 
+#include <memory>
+
 namespace android {
 
 class EGLCacheTest : public ::testing::Test {
@@ -79,23 +83,20 @@
 
     virtual void SetUp() {
         EGLCacheTest::SetUp();
-
-        char* tn = tempnam("/sdcard", "EGL_test-cache-");
-        mFilename = tn;
-        free(tn);
+        mTempFile.reset(new TemporaryFile());
     }
 
     virtual void TearDown() {
-        unlink(mFilename.string());
+        mTempFile.reset(nullptr);
         EGLCacheTest::TearDown();
     }
 
-    String8 mFilename;
+    std::unique_ptr<TemporaryFile> mTempFile;
 };
 
 TEST_F(EGLCacheSerializationTest, ReinitializedCacheContainsValues) {
     uint8_t buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mCache->setCacheFilename(mFilename);
+    mCache->setCacheFilename(&mTempFile->path[0]);
     mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY));
     mCache->setBlob("abcd", 4, "efgh", 4);
     mCache->terminate();
diff --git a/opengl/tests/angeles/demo.c b/opengl/tests/angeles/demo.c
index 802f398..39d871e 100644
--- a/opengl/tests/angeles/demo.c
+++ b/opengl/tests/angeles/demo.c
@@ -666,7 +666,7 @@
         y[2] /= mag;
     }
 
-#define M(row,col)  m[col*4+row]
+#define M(row,col)  m[(col)*4+(row)]
     M(0, 0) = x[0];
     M(0, 1) = x[1];
     M(0, 2) = x[2];
diff --git a/opengl/tests/configdump/configdump.cpp b/opengl/tests/configdump/configdump.cpp
index 69b9eb6..2a94598 100644
--- a/opengl/tests/configdump/configdump.cpp
+++ b/opengl/tests/configdump/configdump.cpp
@@ -18,6 +18,7 @@
 #include <stdio.h>
 
 #include <EGL/egl.h>
+#include <EGL/eglext.h>
 
 #define ATTRIBUTE(_attr) { _attr, #_attr }
 
@@ -26,6 +27,7 @@
     char const* name;
 };
 
+// clang-format off
 Attribute attributes[] = {
         ATTRIBUTE( EGL_BUFFER_SIZE ),
         ATTRIBUTE( EGL_ALPHA_SIZE ),
@@ -60,8 +62,9 @@
         ATTRIBUTE( EGL_RENDERABLE_TYPE ),
         ATTRIBUTE( EGL_MATCH_NATIVE_PIXMAP ),
         ATTRIBUTE( EGL_CONFORMANT ),
+        ATTRIBUTE( EGL_COLOR_COMPONENT_TYPE_EXT ),
 };
-
+// clang-format on
 
 int main(int argc, char** argv)
 {
diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp
index cdbf1cf..9f8d166 100644
--- a/opengl/tests/gl2_basic/gl2_basic.cpp
+++ b/opengl/tests/gl2_basic/gl2_basic.cpp
@@ -44,6 +44,11 @@
     fprintf(stderr, "GL %s = %s\n", name, v);
 }
 
+static void printEGLString(EGLDisplay dpy, const char *name, GLenum s) {
+    const char *v = (const char *) eglQueryString(dpy, s);
+    fprintf(stderr, "GL %s = %s\n", name, v);
+}
+
 static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
     if (returnVal != EGL_TRUE) {
         fprintf(stderr, "%s() returned %d\n", op, returnVal);
@@ -341,6 +346,7 @@
     printGLString("Vendor", GL_VENDOR);
     printGLString("Renderer", GL_RENDERER);
     printGLString("Extensions", GL_EXTENSIONS);
+    printEGLString(dpy, "EGL Extensions", EGL_EXTENSIONS);
 
     if(!setupGraphics(w, h)) {
         fprintf(stderr, "Could not set up graphics.\n");
diff --git a/opengl/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk
index 693fba4..13337c2 100644
--- a/opengl/tests/hwc/Android.mk
+++ b/opengl/tests/hwc/Android.mk
@@ -25,6 +25,8 @@
 LOCAL_C_INCLUDES += system/extras/tests/include \
     $(call include-path-for, opengl-tests-includes) \
 
+LOCAL_STATIC_LIBRARIES := libarect
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/opengl/tests/hwc/hwcColorEquiv.cpp b/opengl/tests/hwc/hwcColorEquiv.cpp
index f1361b8..a9bbcb6 100644
--- a/opengl/tests/hwc/hwcColorEquiv.cpp
+++ b/opengl/tests/hwc/hwcColorEquiv.cpp
@@ -116,7 +116,7 @@
 #define CMD_START_FRAMEWORK  "start 2>&1"
 
 // Macros
-#define NUMA(a) (sizeof(a) / sizeof(a [0])) // Num elements in an array
+#define NUMA(a) (sizeof(a) / sizeof((a)[0])) // Num elements in an array
 #define MEMCLR(addr, size) do { \
         memset((addr), 0, (size)); \
     } while (0)
diff --git a/opengl/tests/hwc/hwcCommit.cpp b/opengl/tests/hwc/hwcCommit.cpp
index 6b287e9..3686dab 100644
--- a/opengl/tests/hwc/hwcCommit.cpp
+++ b/opengl/tests/hwc/hwcCommit.cpp
@@ -156,12 +156,12 @@
 #define CMD_START_FRAMEWORK  "start 2>&1"
 
 // Macros
-#define NUMA(a) (sizeof(a) / sizeof(a [0])) // Num elements in an array
+#define NUMA(a) (sizeof(a) / sizeof((a)[0])) // Num elements in an array
 
 // Local types
 class Rectangle {
 public:
-    Rectangle(uint32_t graphicFormat = defaultFormat,
+    explicit Rectangle(uint32_t graphicFormat = defaultFormat,
               HwcTestDim dfDim = HwcTestDim(1, 1),
               HwcTestDim sDim = HwcTestDim(1, 1));
     void setSourceDim(HwcTestDim dim);
diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp
index 2e2b204..69e56ff 100644
--- a/opengl/tests/hwc/hwcRects.cpp
+++ b/opengl/tests/hwc/hwcRects.cpp
@@ -137,7 +137,7 @@
 #define CMD_START_FRAMEWORK  "start 2>&1"
 
 // Macros
-#define NUMA(a) (sizeof(a) / sizeof(a [0])) // Num elements in an array
+#define NUMA(a) (sizeof(a) / sizeof((a)[0])) // Num elements in an array
 
 // Local types
 class Rectangle {
diff --git a/opengl/tests/hwc/hwcStress.cpp b/opengl/tests/hwc/hwcStress.cpp
index 60c29ef..1469f7c 100644
--- a/opengl/tests/hwc/hwcStress.cpp
+++ b/opengl/tests/hwc/hwcStress.cpp
@@ -162,7 +162,7 @@
 #define CMD_STOP_FRAMEWORK   "stop 2>&1"
 #define CMD_START_FRAMEWORK  "start 2>&1"
 
-#define NUMA(a) (sizeof(a) / sizeof(a [0]))
+#define NUMA(a) (sizeof(a) / sizeof((a)[0]))
 #define MEMCLR(addr, size) do { \
         memset((addr), 0, (size)); \
     } while (0)
diff --git a/opengl/tests/hwc/hwcTestLib.h b/opengl/tests/hwc/hwcTestLib.h
index a942c10..922fc19 100644
--- a/opengl/tests/hwc/hwcTestLib.h
+++ b/opengl/tests/hwc/hwcTestLib.h
@@ -71,7 +71,7 @@
 class ColorRGB {
   public:
     ColorRGB(): _r(0.0), _g(0.0), _b(0.0) {};
-    ColorRGB(float f): _r(f), _g(f), _b(f) {}; // Gray
+    ColorRGB(float f): _r(f), _g(f), _b(f) {}; // Gray, NOLINT(implicit)
     ColorRGB(float r, float g, float b): _r(r), _g(g), _b(b) {};
     float r(void) const { return _r; }
     float g(void) const { return _g; }
diff --git a/opengl/tests/lib/Android.mk b/opengl/tests/lib/Android.mk
index 4407e7b..ea94bc1 100644
--- a/opengl/tests/lib/Android.mk
+++ b/opengl/tests/lib/Android.mk
@@ -24,4 +24,7 @@
 
 LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror
 
+LOCAL_SHARED_LIBRARIES += libgui
+LOCAL_STATIC_LIBRARIES := libarect
+
 include $(BUILD_STATIC_LIBRARY)
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 6caf076..e8691bb 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -985,6 +985,7 @@
         boolean emitExceptionCheck = ((numArrays > 0 || numStrings > 0)
                                              && (hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs)
                                                  || (cfunc.hasPointerArg() && numArrays > 0))
+                                         || (numBufferArgs > 0)
                                          || hasCheckTest(cfunc)
                                          || hasIfTest(cfunc))
                                          || (stringArgs.size() > 0);
@@ -1308,6 +1309,8 @@
 
                     out.println();
                 } else if (jfunc.getArgType(idx).isBuffer()) {
+                    needsExit = needsExit || (!nullAllowed && !isPointerFunc);
+
                     String array = numBufferArgs <= 1 ? "_array" :
                         "_" + cfunc.getArgName(cIndex) + "Array";
                     String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
@@ -1318,6 +1321,17 @@
                         out.println(indent + "if (" + cname + "_buf) {");
                         out.print(indent);
                     }
+                    else
+                    {
+                        out.println(indent + "if (!" + cname + "_buf) {");
+                        out.println(indent + indent + "_exception = 1;");
+                        out.println(indent + indent + "_exceptionType = " +
+                                "\"java/lang/IllegalArgumentException\";");
+                        out.println(indent + indent + "_exceptionMessage = \"" +
+                                cname +" == null\";");
+                        out.println(indent + indent + "goto exit;");
+                        out.println(indent + "}");
+                    }
 
                     if (isPointerFunc) {
                         out.println(indent +
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
index a5a8968..523bc57 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
@@ -28,6 +28,7 @@
     public static final int EGL_CONTEXT_MINOR_VERSION_KHR   = 0x30FB;
     public static final int EGL_CONTEXT_FLAGS_KHR           = 0x30FC;
     public static final int EGL_OPENGL_ES3_BIT_KHR          = 0x0040;
+    public static final int EGL_RECORDABLE_ANDROID          = 0x3142;
 
     native private static void _nativeClassInit();
     static {
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
new file mode 100644
index 0000000..3eacf3c
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
@@ -0,0 +1,9 @@
+/* EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list ) */
+static jobject
+android_eglCreatePixmapSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) {
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
+        "eglCreatePixmapSurface");
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
new file mode 100644
index 0000000..1750b32
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
@@ -0,0 +1,11 @@
+    // C function EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list )
+
+    @Deprecated
+    public static native EGLSurface eglCreatePixmapSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        int pixmap,
+        int[] attrib_list,
+        int offset
+    );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
new file mode 100644
index 0000000..fa260d8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
@@ -0,0 +1 @@
+{"eglCreatePixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;I[II)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePixmapSurface },
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index 18ec5e3..7062c57 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -246,6 +246,19 @@
  */
 static int getNeededCount(GLint pname) {
     int needed = 1;
+#ifdef GL_ES_VERSION_3_0
+    // GLES 3.x pnames
+    switch (pname) {
+        case GL_MAX_VIEWPORT_DIMS:
+            needed = 2;
+            break;
+
+        case GL_PROGRAM_BINARY_FORMATS:
+            glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &needed);
+            break;
+    }
+#endif
+
 #ifdef GL_ES_VERSION_2_0
     // GLES 2.x pnames
     switch (pname) {
diff --git a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
index c966e11..57338c7 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
@@ -1,5 +1,9 @@
     // C function void glGetBufferPointerv ( GLenum target, GLenum pname, GLvoid** params )
 
+    /**
+     * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+     * to be an instance of {@link java.nio.ByteBuffer}.
+     */
     public static native java.nio.Buffer glGetBufferPointerv(
         int target,
         int pname
diff --git a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
index dd656b6..6d42e56 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
@@ -5,15 +5,16 @@
     GLint infoLen = 0;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
     if (!infoLen) {
-        return _env->NewStringUTF("");
+        infoLen = 512;
     }
     char* buf = (char*) malloc(infoLen);
     if (buf == NULL) {
         jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
         return NULL;
     }
-    glGetShaderInfoLog(shader, infoLen, NULL, buf);
-    jstring result = _env->NewStringUTF(buf);
+    GLsizei outLen = 0;
+    glGetShaderInfoLog(shader, infoLen, &outLen, buf);
+    jstring result = _env->NewStringUTF(outLen == 0 ? "" : buf);
     free(buf);
     return result;
 }
diff --git a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
index 482ea99..7b1966b 100644
--- a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
+++ b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
@@ -1,5 +1,9 @@
     // C function GLvoid * glMapBufferRange ( GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access )
 
+    /**
+     * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+     * to be an instance of {@link java.nio.ByteBuffer}.
+     */
     public static native java.nio.Buffer glMapBufferRange(
         int target,
         int offset,
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index f5506ba..026cb37 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -131,6 +131,7 @@
     pointer = _env->CallStaticLongMethod(nioAccessClass,
             getBasePointerID, buffer);
     if (pointer != 0L) {
+        *offset = 0;
         *array = NULL;
         return reinterpret_cast<void *>(pointer);
     }
@@ -138,6 +139,7 @@
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     if (*array == NULL) {
+        *offset = 0;
         return (void*) NULL;
     }
     *offset = _env->CallStaticIntMethod(nioAccessClass,
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index c9384ce..af13395 100755
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -720,6 +720,26 @@
         <enum value="0x332D" name="EGL_YUV_PLANE1_TEXTURE_UNIT_NV"/>
         <enum value="0x332E" name="EGL_YUV_PLANE2_TEXTURE_UNIT_NV"/>
             <unused start="0x332F" end="0x339F"/>
+        <enum value="0x3339" name="EGL_COLOR_COMPONENT_TYPE_EXT"/>
+        <enum value="0x333A" name="EGL_COLOR_COMPONENT_TYPE_FIXED_EXT"/>
+        <enum value="0x333B" name="EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT"/>
+            <unused start="0x333C" end="0x333E"/>
+        <enum value="0x333F" name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+        <enum value="0x3340" name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+        <enum value="0x3341" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+        <enum value="0x3342" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+        <enum value="0x3343" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+        <enum value="0x3344" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+        <enum value="0x3345" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+        <enum value="0x3346" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+        <enum value="0x3347" name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+        <enum value="0x3348" name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+        <enum value="0x3349" name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+        <enum value="0x334A" name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+        <enum value="50000"  name="EGL_METADATA_SCALING_EXT"/>
+            <unused start="0x334B" end="0x334F"/>
+        <enum value="0x3350" name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            <unused start="0x3351" end="0x339F"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -762,8 +782,8 @@
 
 <!-- Reservable for future use. To generate a new range, allocate multiples
      of 16 starting at the lowest available point in this block. -->
-    <enums namespace="EGL" start="0x3420" end="0x3FFF" vendor="KHR">
-            <unused start="0x3420" end="0x3FFF" comment="Reserved for future use"/>
+    <enums namespace="EGL" start="0x3470" end="0x3FFF" vendor="KHR">
+            <unused start="0x3470" end="0x3FFF" comment="Reserved for future use"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -1856,6 +1876,21 @@
                 <command name="eglQueryDisplayAttribEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_pq" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_scrgb_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_image_dma_buf_import" supported="egl">
             <require>
                 <enum name="EGL_LINUX_DMA_BUF_EXT"/>
@@ -1952,6 +1987,22 @@
                 <command name="eglStreamConsumerOutputEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_surface_SMPTE2086_metadata" supported="egl">
+            <require>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+                <enum name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+                <enum name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+                <enum name="EGL_METADATA_SCALING_EXT"/>
+            </require>
+        </extension>
+
         <extension name="EGL_EXT_swap_buffers_with_damage" supported="egl">
             <require>
                 <command name="eglSwapBuffersWithDamageEXT"/>
diff --git a/services/Android.bp b/services/Android.bp
new file mode 100644
index 0000000..7a8ee5d
--- /dev/null
+++ b/services/Android.bp
@@ -0,0 +1 @@
+subdirs = [ "*" ]
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
new file mode 100644
index 0000000..22b084a
--- /dev/null
+++ b/services/audiomanager/Android.bp
@@ -0,0 +1,21 @@
+cc_library_shared {
+    name: "libaudiomanager",
+
+    srcs: [
+        "IAudioManager.cpp",
+        "IPlayer.cpp",
+    ],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+        "liblog",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
new file mode 100644
index 0000000..b9b0706
--- /dev/null
+++ b/services/audiomanager/IAudioManager.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IAudioManager"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
+
+namespace android {
+
+class BpAudioManager : public BpInterface<IAudioManager>
+{
+public:
+    explicit BpAudioManager(const sp<IBinder>& impl)
+        : BpInterface<IAudioManager>(impl)
+    {
+    }
+
+    virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
+            audio_content_type_t content, const sp<IBinder>& player) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+        data.writeInt32(1); // non-null PlayerIdCard parcelable
+        // marshall PlayerIdCard data
+        data.writeInt32((int32_t) playerType);
+        //   write attributes of PlayerIdCard
+        data.writeInt32((int32_t) usage);
+        data.writeInt32((int32_t) content);
+        data.writeInt32(0 /*source: none here, this is a player*/);
+        data.writeInt32(0 /*flags*/);
+        //   write attributes' tags
+        data.writeInt32(1 /*FLATTEN_TAGS*/);
+        data.writeString16(String16("")); // no tags
+        //   write attributes' bundle
+        data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
+        //   write IPlayer
+        data.writeStrongBinder(player);
+        // get new PIId in reply
+        const status_t res = remote()->transact(TRACK_PLAYER, data, &reply, 0);
+        if (res != OK || reply.readExceptionCode() != 0) {
+            ALOGE("trackPlayer() failed, piid is %d", PLAYER_PIID_INVALID);
+            return PLAYER_PIID_INVALID;
+        } else {
+            const audio_unique_id_t piid = (audio_unique_id_t) reply.readInt32();
+            ALOGV("trackPlayer() returned piid %d", piid);
+            return piid;
+        }
+    }
+
+    virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
+            audio_content_type_t content) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+        data.writeInt32((int32_t) piid);
+        data.writeInt32(1); // non-null AudioAttributes parcelable
+        data.writeInt32((int32_t) usage);
+        data.writeInt32((int32_t) content);
+        data.writeInt32(0 /*source: none here, this is a player*/);
+        data.writeInt32(0 /*flags*/);
+        //   write attributes' tags
+        data.writeInt32(1 /*FLATTEN_TAGS*/);
+        data.writeString16(String16("")); // no tags
+        //   write attributes' bundle
+        data.writeInt32(-1977 /*ATTR_PARCEL_IS_NULL_BUNDLE*/); // no bundle
+        return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+        data.writeInt32((int32_t) piid);
+        data.writeInt32((int32_t) event);
+        return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    virtual status_t releasePlayer(audio_unique_id_t piid) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+        data.writeInt32((int32_t) piid);
+        return remote()->transact(RELEASE_PLAYER, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService");
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp
new file mode 100644
index 0000000..e8a9c34
--- /dev/null
+++ b/services/audiomanager/IPlayer.cpp
@@ -0,0 +1,189 @@
+/*
+**
+** 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.
+*/
+
+#define LOG_TAG "IPlayer"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <audiomanager/IPlayer.h>
+
+namespace android {
+
+enum {
+    START      = IBinder::FIRST_CALL_TRANSACTION,
+    PAUSE      = IBinder::FIRST_CALL_TRANSACTION + 1,
+    STOP       = IBinder::FIRST_CALL_TRANSACTION + 2,
+    SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3,
+    SET_PAN    = IBinder::FIRST_CALL_TRANSACTION + 4,
+    SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5,
+    APPLY_VOLUME_SHAPER = IBinder::FIRST_CALL_TRANSACTION + 6,
+};
+
+class BpPlayer : public BpInterface<IPlayer>
+{
+public:
+    explicit BpPlayer(const sp<IBinder>& impl)
+        : BpInterface<IPlayer>(impl)
+    {
+    }
+
+    virtual void start()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(START, data, &reply);
+    }
+
+    virtual void pause()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(PAUSE, data, &reply);
+    }
+
+    virtual void stop()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(STOP, data, &reply);
+    }
+
+    virtual void setVolume(float vol)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeFloat(vol);
+        remote()->transact(SET_VOLUME, data, &reply);
+    }
+
+    virtual void setPan(float pan)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeFloat(pan);
+        remote()->transact(SET_PAN, data, &reply);
+    }
+
+    virtual void setStartDelayMs(int32_t delayMs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeInt32(delayMs);
+        remote()->transact(SET_START_DELAY_MS, data, &reply);
+    }
+
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+
+        status_t status = configuration.get() == nullptr
+                ? data.writeInt32(0)
+                :  data.writeInt32(1)
+                    ?: configuration->writeToParcel(&data);
+        if (status != NO_ERROR) {
+            ALOGW("applyVolumeShaper failed configuration parceling: %d", status);
+            return; // ignore error
+        }
+
+        status = operation.get() == nullptr
+                ? status = data.writeInt32(0)
+                : data.writeInt32(1)
+                    ?: operation->writeToParcel(&data);
+        if (status != NO_ERROR) {
+            ALOGW("applyVolumeShaper failed operation parceling: %d", status);
+            return; // ignore error
+        }
+
+        status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply);
+
+        ALOGW_IF(status != NO_ERROR, "applyVolumeShaper failed transact: %d", status);
+        return; // one way transaction, ignore error
+    }
+};
+
+IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer");
+
+// ----------------------------------------------------------------------
+
+status_t BnPlayer::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+        case START: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            start();
+            return NO_ERROR;
+        } break;
+        case PAUSE: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            pause();
+            return NO_ERROR;
+        }
+        case STOP: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            stop();
+            return NO_ERROR;
+        } break;
+        case SET_VOLUME: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setVolume(data.readFloat());
+            return NO_ERROR;
+        } break;
+        case SET_PAN: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setPan(data.readFloat());
+            return NO_ERROR;
+        } break;
+        case SET_START_DELAY_MS: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setStartDelayMs(data.readInt32());
+            return NO_ERROR;
+        } break;
+        case APPLY_VOLUME_SHAPER: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            sp<VolumeShaper::Configuration> configuration;
+            sp<VolumeShaper::Operation> operation;
+
+            int32_t present;
+            status_t status = data.readInt32(&present);
+            if (status == NO_ERROR && present != 0) {
+                configuration = new VolumeShaper::Configuration();
+                status = configuration->readFromParcel(data);
+            }
+            status = status ?: data.readInt32(&present);
+            if (status == NO_ERROR && present != 0) {
+                operation = new VolumeShaper::Operation();
+                status = operation->readFromParcel(data);
+            }
+            if (status == NO_ERROR) {
+                // one way transaction, no error returned
+                applyVolumeShaper(configuration, operation);
+            }
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+} // namespace android
diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp
new file mode 100644
index 0000000..e441bda
--- /dev/null
+++ b/services/batteryservice/Android.bp
@@ -0,0 +1,22 @@
+cc_library {
+    name: "libbatteryservice",
+
+    srcs: [
+        "BatteryProperties.cpp",
+        "BatteryProperty.cpp",
+        "IBatteryPropertiesListener.cpp",
+        "IBatteryPropertiesRegistrar.cpp",
+    ],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
\ No newline at end of file
diff --git a/services/batteryservice/Android.mk b/services/batteryservice/Android.mk
deleted file mode 100644
index e4097d7..0000000
--- a/services/batteryservice/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    BatteryProperties.cpp \
-    BatteryProperty.cpp \
-    IBatteryPropertiesListener.cpp \
-    IBatteryPropertiesRegistrar.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    libutils \
-    libbinder
-
-LOCAL_MODULE:= libbatteryservice
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp
index d89d4c9..8fa111d 100644
--- a/services/batteryservice/BatteryProperties.cpp
+++ b/services/batteryservice/BatteryProperties.cpp
@@ -41,6 +41,7 @@
     batteryLevel = p->readInt32();
     batteryVoltage = p->readInt32();
     batteryTemperature = p->readInt32();
+    batteryFullCharge = p->readInt32();
     batteryChargeCounter = p->readInt32();
     batteryTechnology = String8((p->readString16()).string());
     return OK;
@@ -58,6 +59,7 @@
     p->writeInt32(batteryLevel);
     p->writeInt32(batteryVoltage);
     p->writeInt32(batteryTemperature);
+    p->writeInt32(batteryFullCharge);
     p->writeInt32(batteryChargeCounter);
     p->writeString16(String16(batteryTechnology));
     return OK;
diff --git a/services/batteryservice/IBatteryPropertiesListener.cpp b/services/batteryservice/IBatteryPropertiesListener.cpp
index 8aff26c..6e5bcfe 100644
--- a/services/batteryservice/IBatteryPropertiesListener.cpp
+++ b/services/batteryservice/IBatteryPropertiesListener.cpp
@@ -24,7 +24,7 @@
 class BpBatteryPropertiesListener : public BpInterface<IBatteryPropertiesListener>
 {
 public:
-    BpBatteryPropertiesListener(const sp<IBinder>& impl)
+    explicit BpBatteryPropertiesListener(const sp<IBinder>& impl)
         : BpInterface<IBatteryPropertiesListener>(impl)
     {
     }
@@ -43,4 +43,22 @@
 
 // ----------------------------------------------------------------------------
 
+status_t BnBatteryPropertiesListener::onTransact(uint32_t code, const Parcel& data,
+                                                 Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACT_BATTERYPROPERTIESCHANGED: {
+            CHECK_INTERFACE(IBatteryPropertiesListener, data, reply);
+            struct BatteryProperties props = {};
+            if (data.readInt32() != 0) {
+                props.readFromParcel((Parcel*)&data);
+            }
+            batteryPropertiesChanged(props);
+            return NO_ERROR;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+};
+
 }; // namespace android
diff --git a/services/batteryservice/IBatteryPropertiesRegistrar.cpp b/services/batteryservice/IBatteryPropertiesRegistrar.cpp
index 46934e0..01a65ae 100644
--- a/services/batteryservice/IBatteryPropertiesRegistrar.cpp
+++ b/services/batteryservice/IBatteryPropertiesRegistrar.cpp
@@ -28,7 +28,7 @@
 
 class BpBatteryPropertiesRegistrar : public BpInterface<IBatteryPropertiesRegistrar> {
 public:
-    BpBatteryPropertiesRegistrar(const sp<IBinder>& impl)
+    explicit BpBatteryPropertiesRegistrar(const sp<IBinder>& impl)
         : BpInterface<IBatteryPropertiesRegistrar>(impl) {}
 
         void registerListener(const sp<IBatteryPropertiesListener>& listener) {
@@ -60,6 +60,12 @@
                 val->readFromParcel(&reply);
             return ret;
         }
+
+        void scheduleUpdate() {
+            Parcel data;
+            data.writeInterfaceToken(IBatteryPropertiesRegistrar::getInterfaceDescriptor());
+            remote()->transact(SCHEDULE_UPDATE, data, NULL);
+        }
 };
 
 IMPLEMENT_META_INTERFACE(BatteryPropertiesRegistrar, "android.os.IBatteryPropertiesRegistrar");
@@ -97,6 +103,12 @@
             val.writeToParcel(reply);
             return OK;
         }
+
+        case SCHEDULE_UPDATE: {
+            CHECK_INTERFACE(IBatteryPropertiesRegistrar, data, reply);
+            scheduleUpdate();
+            return OK;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 };
diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp
new file mode 100644
index 0000000..3442cb2
--- /dev/null
+++ b/services/displayservice/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+    name: "libdisplayservicehidl",
+
+    srcs: [
+        "DisplayService.cpp",
+        "DisplayEventReceiver.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libgui",
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+        "android.frameworks.displayservice@1.0",
+    ],
+
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "android.frameworks.displayservice@1.0",
+        "libgui",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ]
+}
diff --git a/services/displayservice/DisplayEventReceiver.cpp b/services/displayservice/DisplayEventReceiver.cpp
new file mode 100644
index 0000000..5993e44
--- /dev/null
+++ b/services/displayservice/DisplayEventReceiver.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libdisplayservicehidl"
+
+#include <displayservice/DisplayEventReceiver.h>
+
+#include <android-base/logging.h>
+#include <android/frameworks/displayservice/1.0/BpHwEventCallback.h>
+
+#include <thread>
+
+namespace android {
+namespace frameworks {
+namespace displayservice {
+namespace V1_0 {
+namespace implementation {
+
+sp<Looper> getLooper() {
+    static sp<Looper> looper = []() {
+        sp<Looper> looper = new Looper(false /* allowNonCallbacks */);
+
+        std::thread{[&](){
+            int pollResult = looper->pollAll(-1 /* timeout */);
+            LOG(ERROR) << "Looper::pollAll returns unexpected " << pollResult;
+        }}.detach();
+
+        return looper;
+    }();
+
+    return looper;
+}
+
+DisplayEventReceiver::AttachedEvent::AttachedEvent(const sp<IEventCallback> &callback)
+    : mCallback(callback)
+{
+    mLooperAttached = getLooper()->addFd(mFwkReceiver.getFd(),
+            Looper::POLL_CALLBACK,
+            Looper::EVENT_INPUT,
+            this,
+            nullptr);
+}
+
+DisplayEventReceiver::AttachedEvent::~AttachedEvent() {
+    if (!detach()) {
+        LOG(ERROR) << "Could not remove fd from looper.";
+    }
+}
+
+bool DisplayEventReceiver::AttachedEvent::detach() {
+    if (!valid()) {
+        return true;
+    }
+
+    return getLooper()->removeFd(mFwkReceiver.getFd());
+}
+
+bool DisplayEventReceiver::AttachedEvent::valid() const {
+    return mFwkReceiver.initCheck() == OK && mLooperAttached;
+}
+
+DisplayEventReceiver::FwkReceiver &DisplayEventReceiver::AttachedEvent::receiver() {
+    return mFwkReceiver;
+}
+
+int DisplayEventReceiver::AttachedEvent::handleEvent(int fd, int events, void* /* data */) {
+    CHECK(fd == mFwkReceiver.getFd());
+
+    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+        LOG(ERROR) << "AttachedEvent handleEvent received error or hangup:" << events;
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        LOG(ERROR) << "AttachedEvent handleEvent unhandled poll event:" << events;
+        return 1; // keep the callback
+    }
+
+    constexpr size_t SIZE = 1;
+
+    ssize_t n;
+    FwkReceiver::Event buf[SIZE];
+    while ((n = mFwkReceiver.getEvents(buf, SIZE)) > 0) {
+        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
+            const FwkReceiver::Event &event = buf[i];
+
+            uint32_t type = event.header.type;
+            uint64_t timestamp = event.header.timestamp;
+
+            switch(buf[i].header.type) {
+                case FwkReceiver::DISPLAY_EVENT_VSYNC: {
+                    mCallback->onVsync(timestamp, event.vsync.count);
+                } break;
+                case FwkReceiver::DISPLAY_EVENT_HOTPLUG: {
+                    mCallback->onHotplug(timestamp, event.hotplug.connected);
+                } break;
+                default: {
+                    LOG(ERROR) << "AttachedEvent handleEvent unknown type: " << type;
+                }
+            }
+        }
+    }
+
+    return 1; // keep on going
+}
+
+Return<Status> DisplayEventReceiver::init(const sp<IEventCallback>& callback) {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    if (mAttached != nullptr || callback == nullptr) {
+        return Status::BAD_VALUE;
+    }
+
+    mAttached = new AttachedEvent(callback);
+
+    return mAttached->valid() ? Status::SUCCESS : Status::UNKNOWN;
+}
+
+Return<Status> DisplayEventReceiver::setVsyncRate(int32_t count) {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    if (mAttached == nullptr || count < 0) {
+        return Status::BAD_VALUE;
+    }
+
+    bool success = OK == mAttached->receiver().setVsyncRate(count);
+    return success ? Status::SUCCESS : Status::UNKNOWN;
+}
+
+Return<Status> DisplayEventReceiver::requestNextVsync() {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    if (mAttached == nullptr) {
+        return Status::BAD_VALUE;
+    }
+
+    bool success = OK == mAttached->receiver().requestNextVsync();
+    return success ? Status::SUCCESS : Status::UNKNOWN;
+}
+
+Return<Status> DisplayEventReceiver::close() {
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mAttached == nullptr) {
+        return Status::BAD_VALUE;
+    }
+
+    bool success = mAttached->detach();
+    mAttached = nullptr;
+
+    return success ? Status::SUCCESS : Status::UNKNOWN;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace displayservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/displayservice/DisplayService.cpp b/services/displayservice/DisplayService.cpp
new file mode 100644
index 0000000..18418fd
--- /dev/null
+++ b/services/displayservice/DisplayService.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <displayservice/DisplayService.h>
+#include <displayservice/DisplayEventReceiver.h>
+
+namespace android {
+namespace frameworks {
+namespace displayservice {
+namespace V1_0 {
+namespace implementation {
+
+Return<sp<IDisplayEventReceiver>> DisplayService::getEventReceiver() {
+    return new DisplayEventReceiver();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace displayservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/displayservice/include/displayservice/DisplayEventReceiver.h b/services/displayservice/include/displayservice/DisplayEventReceiver.h
new file mode 100644
index 0000000..5d569b6
--- /dev/null
+++ b/services/displayservice/include/displayservice/DisplayEventReceiver.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYEVENTRECEIVER_H
+#define ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYEVENTRECEIVER_H
+
+#include <android/frameworks/displayservice/1.0/IDisplayEventReceiver.h>
+#include <gui/DisplayEventReceiver.h>
+#include <hidl/Status.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+
+namespace android {
+namespace frameworks {
+namespace displayservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+class DisplayEventReceiver : public IDisplayEventReceiver {
+public:
+    Return<Status> init(const sp<IEventCallback>& callback) override;
+    Return<Status> setVsyncRate(int32_t count) override;
+    Return<Status> requestNextVsync() override;
+    Return<Status> close() override;
+
+private:
+    using FwkReceiver = ::android::DisplayEventReceiver;
+
+    struct AttachedEvent : LooperCallback {
+        AttachedEvent(const sp<IEventCallback> &callback);
+        ~AttachedEvent();
+
+        bool detach();
+        bool valid() const;
+        FwkReceiver &receiver();
+        virtual int handleEvent(int fd, int events, void* /* data */) override;
+
+    private:
+        FwkReceiver mFwkReceiver;
+        sp<IEventCallback> mCallback;
+        bool mLooperAttached;
+    };
+
+    sp<AttachedEvent> mAttached;
+    std::mutex mMutex;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace displayservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYEVENTRECEIVER_H
diff --git a/services/displayservice/include/displayservice/DisplayService.h b/services/displayservice/include/displayservice/DisplayService.h
new file mode 100644
index 0000000..9722e71
--- /dev/null
+++ b/services/displayservice/include/displayservice/DisplayService.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYSERVICE_H
+#define ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYSERVICE_H
+
+#include <android/frameworks/displayservice/1.0/IDisplayService.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace frameworks {
+namespace displayservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct DisplayService : public IDisplayService {
+    Return<sp<IDisplayEventReceiver>> getEventReceiver() override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace displayservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_DISPLAYSERVICE_V1_0_DISPLAYSERVICE_H
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index 2a53dec..d2f8995 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -55,10 +55,10 @@
  * operation with a byte that only has the relevant bit set.
  * eg. to check for the 12th bit, we do (array[1] & 1<<4)
  */
-#define test_bit(bit, array)    (array[bit/8] & (1<<(bit%8)))
+#define test_bit(bit, array)    ((array)[(bit)/8] & (1<<((bit)%8)))
 
 /* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits)  ((bits + 7) / 8)
+#define sizeof_bit_array(bits)  (((bits) + 7) / 8)
 
 #define INDENT "  "
 #define INDENT2 "    "
diff --git a/services/inputflinger/InputApplication.cpp b/services/inputflinger/InputApplication.cpp
index a99e637..9e90631 100644
--- a/services/inputflinger/InputApplication.cpp
+++ b/services/inputflinger/InputApplication.cpp
@@ -18,7 +18,7 @@
 
 #include "InputApplication.h"
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index e48c53a..2efb340 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -45,16 +45,16 @@
 
 #include "InputDispatcher.h"
 
-#include <utils/Trace.h>
-#include <cutils/log.h>
-#include <powermanager/PowerManager.h>
-#include <ui/Region.h>
-
-#include <stddef.h>
-#include <unistd.h>
 #include <errno.h>
 #include <limits.h>
+#include <stddef.h>
 #include <time.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <powermanager/PowerManager.h>
+#include <ui/Region.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -902,7 +902,7 @@
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
                 "x=%f, y=%f, pressure=%f, size=%f, "
                 "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-                "orientation=%f, relativeX=%f, relativeY=%f",
+                "orientation=%f",
                 i, entry->pointerProperties[i].id,
                 entry->pointerProperties[i].toolType,
                 entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
@@ -913,9 +913,7 @@
                 entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
                 entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
                 entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
 }
@@ -1174,10 +1172,11 @@
     bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        if (switchedDevice && mTempTouchState.down && !down) {
+        if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
 #if DEBUG_FOCUS
             ALOGD("Dropping event because a pointer for a different device is already down.");
 #endif
+            // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             switchedDevice = false;
             wrongDevice = true;
@@ -1189,6 +1188,15 @@
         mTempTouchState.source = entry->source;
         mTempTouchState.displayId = displayId;
         isSplit = false;
+    } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+#if DEBUG_FOCUS
+        ALOGI("Dropping move event because a pointer for a different device is already active.");
+#endif
+        // TODO: test multiple simultaneous input streams.
+        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        switchedDevice = false;
+        wrongDevice = true;
+        goto Failed;
     }
 
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
@@ -2418,7 +2426,7 @@
             struct KeyReplacement replacement = {keyCode, args->deviceId};
             mReplacedKeys.add(replacement, newKeyCode);
             keyCode = newKeyCode;
-            metaState &= ~AMETA_META_ON;
+            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
     } else if (args->action == AKEY_EVENT_ACTION_UP) {
         // In order to maintain a consistent stream of up and down events, check to see if the key
@@ -2430,7 +2438,7 @@
         if (index >= 0) {
             keyCode = mReplacedKeys.valueAt(index);
             mReplacedKeys.removeItemsAt(index);
-            metaState &= ~AMETA_META_ON;
+            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
     }
 
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 1c054f5..90c69ce 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -455,7 +455,7 @@
     };
 
     struct ConfigurationChangedEntry : EventEntry {
-        ConfigurationChangedEntry(nsecs_t eventTime);
+        explicit ConfigurationChangedEntry(nsecs_t eventTime);
         virtual void appendDescription(String8& msg) const;
 
     protected:
@@ -591,7 +591,7 @@
 
     class Connection;
     struct CommandEntry : Link<CommandEntry> {
-        CommandEntry(Command command);
+        explicit CommandEntry(Command command);
         ~CommandEntry();
 
         Command command;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index dded47d..2ee222b 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -20,7 +20,7 @@
 
 #include "InputListener.h"
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/InputListener.h
index 1ec09ce..ea3dd1c 100644
--- a/services/inputflinger/InputListener.h
+++ b/services/inputflinger/InputListener.h
@@ -40,7 +40,7 @@
 
     inline NotifyConfigurationChangedArgs() { }
 
-    NotifyConfigurationChangedArgs(nsecs_t eventTime);
+    explicit NotifyConfigurationChangedArgs(nsecs_t eventTime);
 
     NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
 
@@ -178,7 +178,7 @@
     virtual ~QueuedInputListener();
 
 public:
-    QueuedInputListener(const sp<InputListenerInterface>& innerListener);
+    explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
     virtual void notifyKey(const NotifyKeyArgs* args);
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 6a6547b..519faa6 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,7 @@
 
 #include "InputManager.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index b9be675..73255dd 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -44,17 +44,18 @@
 
 #include "InputReader.h"
 
-#include <cutils/log.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
+#include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <math.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <math.h>
+
+#include <log/log.h>
+
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -226,18 +227,65 @@
 
 // --- InputReaderConfiguration ---
 
-bool InputReaderConfiguration::getDisplayInfo(bool external, DisplayViewport* outViewport) const {
-    const DisplayViewport& viewport = external ? mExternalDisplay : mInternalDisplay;
-    if (viewport.displayId >= 0) {
-        *outViewport = viewport;
+bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType,
+        const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
+    const DisplayViewport* viewport = NULL;
+    if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
+        for (DisplayViewport currentViewport : mVirtualDisplays) {
+            if (currentViewport.uniqueId == *uniqueDisplayId) {
+                viewport = &currentViewport;
+                break;
+            }
+        }
+    } else if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
+        viewport = &mExternalDisplay;
+    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
+        viewport = &mInternalDisplay;
+    }
+
+    if (viewport != NULL && viewport->displayId >= 0) {
+        *outViewport = *viewport;
         return true;
     }
     return false;
 }
 
-void InputReaderConfiguration::setDisplayInfo(bool external, const DisplayViewport& viewport) {
-    DisplayViewport& v = external ? mExternalDisplay : mInternalDisplay;
-    v = viewport;
+void InputReaderConfiguration::setPhysicalDisplayViewport(ViewportType viewportType,
+        const DisplayViewport& viewport) {
+    if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
+        mExternalDisplay = viewport;
+    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
+        mInternalDisplay = viewport;
+    }
+}
+
+void InputReaderConfiguration::setVirtualDisplayViewports(
+        const Vector<DisplayViewport>& viewports) {
+    mVirtualDisplays = viewports;
+}
+
+void InputReaderConfiguration::dump(String8& dump) const {
+    dump.append(INDENT4 "ViewportInternal:\n");
+    dumpViewport(dump, mInternalDisplay);
+    dump.append(INDENT4 "ViewportExternal:\n");
+    dumpViewport(dump, mExternalDisplay);
+    dump.append(INDENT4 "ViewportVirtual:\n");
+    for (const DisplayViewport& viewport : mVirtualDisplays) {
+        dumpViewport(dump, viewport);
+    }
+}
+
+void InputReaderConfiguration::dumpViewport(String8& dump, const DisplayViewport& viewport) const {
+    dump.appendFormat(INDENT5 "Viewport: displayId=%d, orientation=%d, uniqueId='%s', "
+            "logicalFrame=[%d, %d, %d, %d], "
+            "physicalFrame=[%d, %d, %d, %d], "
+            "deviceSize=[%d, %d]\n",
+            viewport.displayId, viewport.orientation, viewport.uniqueId.c_str(),
+            viewport.logicalLeft, viewport.logicalTop,
+            viewport.logicalRight, viewport.logicalBottom,
+            viewport.physicalLeft, viewport.physicalTop,
+            viewport.physicalRight, viewport.physicalBottom,
+            viewport.deviceWidth, viewport.deviceHeight);
 }
 
 
@@ -850,6 +898,9 @@
             mConfig.pointerGestureMovementSpeedRatio);
     dump.appendFormat(INDENT3 "ZoomSpeedRatio: %0.1f\n",
             mConfig.pointerGestureZoomSpeedRatio);
+
+    dump.append(INDENT3 "Viewports:\n");
+    mConfig.dump(dump);
 }
 
 void InputReader::monitor() {
@@ -2160,7 +2211,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             DisplayViewport v;
-            if (config->getDisplayInfo(false /*external*/, &v)) {
+            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
                 mOrientation = v.orientation;
             } else {
                 mOrientation = DISPLAY_ORIENTATION_0;
@@ -2321,10 +2372,6 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    if (down && !isMetaKey(keyCode)) {
-        getContext()->fadePointer();
-    }
-
     NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
@@ -2477,6 +2524,11 @@
 
         // Configure device mode.
         switch (mParameters.mode) {
+        case Parameters::MODE_POINTER_RELATIVE:
+            // Should not happen during first time configuration.
+            ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
+            mParameters.mode = Parameters::MODE_POINTER;
+            // fall through.
         case Parameters::MODE_POINTER:
             mSource = AINPUT_SOURCE_MOUSE;
             mXPrecision = 1.0f;
@@ -2498,6 +2550,31 @@
         mHWheelScale = 1.0f;
     }
 
+    if ((!changes && config->pointerCapture)
+            || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
+        if (config->pointerCapture) {
+            if (mParameters.mode == Parameters::MODE_POINTER) {
+                mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+                mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
+                // Keep PointerController around in order to preserve the pointer position.
+                mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            } else {
+                ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
+            }
+        } else {
+            if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
+                mParameters.mode = Parameters::MODE_POINTER;
+                mSource = AINPUT_SOURCE_MOUSE;
+            } else {
+                ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
+            }
+        }
+        bumpGeneration();
+        if (changes) {
+            getDevice()->notifyReset(when);
+        }
+    }
+
     if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
         mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
         mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
@@ -2507,7 +2584,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             DisplayViewport v;
-            if (config->getDisplayInfo(false /*external*/, &v)) {
+            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
                 mOrientation = v.orientation;
             } else {
                 mOrientation = DISPLAY_ORIENTATION_0;
@@ -2549,6 +2626,9 @@
     case Parameters::MODE_POINTER:
         dump.append(INDENT4 "Mode: pointer\n");
         break;
+    case Parameters::MODE_POINTER_RELATIVE:
+        dump.append(INDENT4 "Mode: relative pointer\n");
+        break;
     case Parameters::MODE_NAVIGATION:
         dump.append(INDENT4 "Mode: navigation\n");
         break;
@@ -2635,7 +2715,7 @@
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
     int32_t displayId;
-    if (mPointerController != NULL) {
+    if (mSource == AINPUT_SOURCE_MOUSE) {
         if (moved || scrolled || buttonsChanged) {
             mPointerController->setPresentation(
                     PointerControllerInterface::PRESENTATION_POINTER);
@@ -2686,7 +2766,7 @@
         int32_t motionEventAction;
         if (downChanged) {
             motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
-        } else if (down || mPointerController == NULL) {
+        } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
             motionEventAction = AMOTION_EVENT_ACTION_MOVE;
         } else {
             motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
@@ -2731,7 +2811,7 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP
-                && mPointerController != NULL) {
+                && (mSource == AINPUT_SOURCE_MOUSE)) {
             NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                     metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
@@ -2949,7 +3029,7 @@
 }
 
 void TouchInputMapper::dump(String8& dump) {
-    dump.append(INDENT2 "Touch Input Mapper:\n");
+    dump.appendFormat(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
     dumpParameters(dump);
     dumpVirtualKeys(dump);
     dumpRawPointerAxes(dump);
@@ -3041,6 +3121,22 @@
     }
 }
 
+const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
+    switch (deviceMode) {
+    case DEVICE_MODE_DISABLED:
+        return "disabled";
+    case DEVICE_MODE_DIRECT:
+        return "direct";
+    case DEVICE_MODE_UNSCALED:
+        return "unscaled";
+    case DEVICE_MODE_NAVIGATION:
+        return "navigation";
+    case DEVICE_MODE_POINTER:
+        return "pointer";
+    }
+    return "unknown";
+}
+
 void TouchInputMapper::configure(nsecs_t when,
         const InputReaderConfiguration* config, uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -3166,9 +3262,11 @@
             || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
             || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
         mParameters.hasAssociatedDisplay = true;
-        mParameters.associatedDisplayIsExternal =
-                mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
-                        && getDevice()->isExternal();
+        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+            mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+            getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
+                    mParameters.uniqueDisplayId);
+        }
     }
 
     // Initial downs on external touch devices should wake the device.
@@ -3210,9 +3308,11 @@
         ALOG_ASSERT(false);
     }
 
-    dump.appendFormat(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s\n",
+    dump.appendFormat(
+            INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n",
             toString(mParameters.hasAssociatedDisplay),
-            toString(mParameters.associatedDisplayIsExternal));
+            toString(mParameters.associatedDisplayIsExternal),
+            mParameters.uniqueDisplayId.c_str());
     dump.appendFormat(INDENT4 "OrientationAware: %s\n",
             toString(mParameters.orientationAware));
 }
@@ -3288,7 +3388,21 @@
     // Get associated display dimensions.
     DisplayViewport newViewport;
     if (mParameters.hasAssociatedDisplay) {
-        if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal, &newViewport)) {
+        const String8* uniqueDisplayId = NULL;
+        ViewportType viewportTypeToUse;
+
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else if (!mParameters.uniqueDisplayId.isEmpty()) {
+            // If the IDC file specified a unique display Id, then it expects to be linked to a
+            // virtual display with the same unique ID.
+            uniqueDisplayId = &mParameters.uniqueDisplayId;
+            viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+
+        if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &newViewport)) {
             ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
                     "display.  The device will be inoperable until the display size "
                     "becomes available.",
@@ -5421,8 +5535,6 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
     } else if (currentFingerCount == 0) {
         // Case 3. No fingers down and button is not pressed. (NEUTRAL)
         if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
@@ -5581,10 +5693,6 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                 down ? 1.0f : 0.0f);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(
-                AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(
-                AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
 
         if (lastFingerCount == 0 && currentFingerCount != 0) {
             mPointerGesture.resetTap();
@@ -5831,10 +5939,6 @@
                     mPointerGesture.referenceGestureX);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                     mPointerGesture.referenceGestureY);
-            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
-                    commonDeltaX);
-            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
-                    commonDeltaY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
         } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
             // FREEFORM mode.
@@ -5931,10 +6035,6 @@
                         AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY);
                 mPointerGesture.currentGestureCoords[i].setAxisValue(
                         AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-                mPointerGesture.currentGestureCoords[i].setAxisValue(
-                        AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
-                mPointerGesture.currentGestureCoords[i].setAxisValue(
-                        AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
             }
 
             if (mPointerGesture.activeGestureId < 0) {
@@ -6057,8 +6157,6 @@
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                 hovering ? 0.0f : 1.0f);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, x);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, y);
         mPointerSimple.currentProperties.id = 0;
         mPointerSimple.currentProperties.toolType =
                 mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
@@ -6652,6 +6750,7 @@
     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
     size_t outCount = 0;
     BitSet32 newPointerIdBits;
+    mHavePointerIds = true;
 
     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
         const MultiTouchMotionAccumulator::Slot* inSlot =
@@ -6696,33 +6795,33 @@
         outPointer.isHovering = isHovering;
 
         // Assign pointer id using tracking id if available.
-        mHavePointerIds = true;
-        int32_t trackingId = inSlot->getTrackingId();
-        int32_t id = -1;
-        if (trackingId >= 0) {
-            for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
-                uint32_t n = idBits.clearFirstMarkedBit();
-                if (mPointerTrackingIdMap[n] == trackingId) {
-                    id = n;
+        if (mHavePointerIds) {
+            int32_t trackingId = inSlot->getTrackingId();
+            int32_t id = -1;
+            if (trackingId >= 0) {
+                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
+                    uint32_t n = idBits.clearFirstMarkedBit();
+                    if (mPointerTrackingIdMap[n] == trackingId) {
+                        id = n;
+                    }
+                }
+
+                if (id < 0 && !mPointerIdBits.isFull()) {
+                    id = mPointerIdBits.markFirstUnmarkedBit();
+                    mPointerTrackingIdMap[id] = trackingId;
                 }
             }
-
-            if (id < 0 && !mPointerIdBits.isFull()) {
-                id = mPointerIdBits.markFirstUnmarkedBit();
-                mPointerTrackingIdMap[id] = trackingId;
+            if (id < 0) {
+                mHavePointerIds = false;
+                outState->rawPointerData.clearIdBits();
+                newPointerIdBits.clear();
+            } else {
+                outPointer.id = id;
+                outState->rawPointerData.idToIndex[id] = outCount;
+                outState->rawPointerData.markIdBit(id, isHovering);
+                newPointerIdBits.markBit(id);
             }
         }
-        if (id < 0) {
-            mHavePointerIds = false;
-            outState->rawPointerData.clearIdBits();
-            newPointerIdBits.clear();
-        } else {
-            outPointer.id = id;
-            outState->rawPointerData.idToIndex[id] = outCount;
-            outState->rawPointerData.markIdBit(id, isHovering);
-            newPointerIdBits.markBit(id);
-        }
-
         outCount += 1;
     }
 
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 076f3d6..edb6fcc 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -21,6 +21,7 @@
 #include "PointerControllerInterface.h"
 #include "InputListener.h"
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
@@ -49,70 +50,6 @@
 class InputMapper;
 
 /*
- * Describes how coordinates are mapped on a physical display.
- * See com.android.server.display.DisplayViewport.
- */
-struct DisplayViewport {
-    int32_t displayId; // -1 if invalid
-    int32_t orientation;
-    int32_t logicalLeft;
-    int32_t logicalTop;
-    int32_t logicalRight;
-    int32_t logicalBottom;
-    int32_t physicalLeft;
-    int32_t physicalTop;
-    int32_t physicalRight;
-    int32_t physicalBottom;
-    int32_t deviceWidth;
-    int32_t deviceHeight;
-
-    DisplayViewport() :
-            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
-            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
-            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0) {
-    }
-
-    bool operator==(const DisplayViewport& other) const {
-        return displayId == other.displayId
-                && orientation == other.orientation
-                && logicalLeft == other.logicalLeft
-                && logicalTop == other.logicalTop
-                && logicalRight == other.logicalRight
-                && logicalBottom == other.logicalBottom
-                && physicalLeft == other.physicalLeft
-                && physicalTop == other.physicalTop
-                && physicalRight == other.physicalRight
-                && physicalBottom == other.physicalBottom
-                && deviceWidth == other.deviceWidth
-                && deviceHeight == other.deviceHeight;
-    }
-
-    bool operator!=(const DisplayViewport& other) const {
-        return !(*this == other);
-    }
-
-    inline bool isValid() const {
-        return displayId >= 0;
-    }
-
-    void setNonDisplayViewport(int32_t width, int32_t height) {
-        displayId = ADISPLAY_ID_NONE;
-        orientation = DISPLAY_ORIENTATION_0;
-        logicalLeft = 0;
-        logicalTop = 0;
-        logicalRight = width;
-        logicalBottom = height;
-        physicalLeft = 0;
-        physicalTop = 0;
-        physicalRight = width;
-        physicalBottom = height;
-        deviceWidth = width;
-        deviceHeight = height;
-    }
-};
-
-/*
  * Input reader configuration.
  *
  * Specifies various options that modify the behavior of the input reader.
@@ -144,6 +81,9 @@
         // The presence of an external stylus has changed.
         CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
 
+        // The pointer capture mode has changed.
+        CHANGE_POINTER_CAPTURE = 1 << 8,
+
         // All devices must be reopened.
         CHANGE_MUST_REOPEN = 1 << 31,
     };
@@ -231,6 +171,9 @@
     // True to show the location of touches on the touch screen as spots.
     bool showTouches;
 
+    // True if pointer capture is enabled.
+    bool pointerCapture;
+
     InputReaderConfiguration() :
             virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
@@ -249,12 +192,19 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false) { }
 
-    bool getDisplayInfo(bool external, DisplayViewport* outViewport) const;
-    void setDisplayInfo(bool external, const DisplayViewport& viewport);
+    bool getDisplayViewport(ViewportType viewportType, const String8* displayId,
+            DisplayViewport* outViewport) const;
+    void setPhysicalDisplayViewport(ViewportType viewportType, const DisplayViewport& viewport);
+    void setVirtualDisplayViewports(const Vector<DisplayViewport>& viewports);
+
+
+    void dump(String8& dump) const;
+    void dumpViewport(String8& dump, const DisplayViewport& viewport) const;
 
 private:
     DisplayViewport mInternalDisplay;
     DisplayViewport mExternalDisplay;
+    Vector<DisplayViewport> mVirtualDisplays;
 };
 
 
@@ -484,7 +434,7 @@
         InputReader* mReader;
 
     public:
-        ContextImpl(InputReader* reader);
+        explicit ContextImpl(InputReader* reader);
 
         virtual void updateGlobalMetaState();
         virtual int32_t getGlobalMetaState();
@@ -568,7 +518,7 @@
 /* Reads raw events from the event hub and processes them, endlessly. */
 class InputReaderThread : public Thread {
 public:
-    InputReaderThread(const sp<InputReaderInterface>& reader);
+    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
     virtual ~InputReaderThread();
 
 private:
@@ -1007,7 +957,7 @@
  */
 class InputMapper {
 public:
-    InputMapper(InputDevice* device);
+    explicit InputMapper(InputDevice* device);
     virtual ~InputMapper();
 
     inline InputDevice* getDevice() { return mDevice; }
@@ -1058,7 +1008,7 @@
 
 class SwitchInputMapper : public InputMapper {
 public:
-    SwitchInputMapper(InputDevice* device);
+    explicit SwitchInputMapper(InputDevice* device);
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources();
@@ -1078,7 +1028,7 @@
 
 class VibratorInputMapper : public InputMapper {
 public:
-    VibratorInputMapper(InputDevice* device);
+    explicit VibratorInputMapper(InputDevice* device);
     virtual ~VibratorInputMapper();
 
     virtual uint32_t getSources();
@@ -1178,7 +1128,7 @@
 
 class CursorInputMapper : public InputMapper {
 public:
-    CursorInputMapper(InputDevice* device);
+    explicit CursorInputMapper(InputDevice* device);
     virtual ~CursorInputMapper();
 
     virtual uint32_t getSources();
@@ -1200,6 +1150,7 @@
     struct Parameters {
         enum Mode {
             MODE_POINTER,
+            MODE_POINTER_RELATIVE,
             MODE_NAVIGATION,
         };
 
@@ -1243,7 +1194,7 @@
 
 class RotaryEncoderInputMapper : public InputMapper {
 public:
-    RotaryEncoderInputMapper(InputDevice* device);
+    explicit RotaryEncoderInputMapper(InputDevice* device);
     virtual ~RotaryEncoderInputMapper();
 
     virtual uint32_t getSources();
@@ -1264,7 +1215,7 @@
 
 class TouchInputMapper : public InputMapper {
 public:
-    TouchInputMapper(InputDevice* device);
+    explicit TouchInputMapper(InputDevice* device);
     virtual ~TouchInputMapper();
 
     virtual uint32_t getSources();
@@ -1334,6 +1285,7 @@
         bool associatedDisplayIsExternal;
         bool orientationAware;
         bool hasButtonUnderPad;
+        String8 uniqueDisplayId;
 
         enum GestureMode {
             GESTURE_MODE_SINGLE_TOUCH,
@@ -1882,12 +1834,14 @@
     const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
 
     static void assignPointerIds(const RawState* last, RawState* current);
+
+    const char* modeToString(DeviceMode deviceMode);
 };
 
 
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
-    SingleTouchInputMapper(InputDevice* device);
+    explicit SingleTouchInputMapper(InputDevice* device);
     virtual ~SingleTouchInputMapper();
 
     virtual void reset(nsecs_t when);
@@ -1905,7 +1859,7 @@
 
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
-    MultiTouchInputMapper(InputDevice* device);
+    explicit MultiTouchInputMapper(InputDevice* device);
     virtual ~MultiTouchInputMapper();
 
     virtual void reset(nsecs_t when);
@@ -1926,7 +1880,7 @@
 
 class ExternalStylusInputMapper : public InputMapper {
 public:
-    ExternalStylusInputMapper(InputDevice* device);
+    explicit ExternalStylusInputMapper(InputDevice* device);
     virtual ~ExternalStylusInputMapper() = default;
 
     virtual uint32_t getSources();
@@ -1948,7 +1902,7 @@
 
 class JoystickInputMapper : public InputMapper {
 public:
-    JoystickInputMapper(InputDevice* device);
+    explicit JoystickInputMapper(InputDevice* device);
     virtual ~JoystickInputMapper();
 
     virtual uint32_t getSources();
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index d7b514b..5e82d75 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -19,7 +19,7 @@
 
 #include "InputWindow.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <ui/Rect.h>
 #include <ui/Region.h>
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index e243637..feca6cf 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -196,7 +196,7 @@
     void releaseInfo();
 
 protected:
-    InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+    explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
     virtual ~InputWindowHandle();
 
     InputWindowInfo* mInfo;
diff --git a/services/inputflinger/host/InputDriver.h b/services/inputflinger/host/InputDriver.h
index 8d5a31e..e56673b 100644
--- a/services/inputflinger/host/InputDriver.h
+++ b/services/inputflinger/host/InputDriver.h
@@ -82,7 +82,7 @@
 
 class InputDriver : public InputDriverInterface {
 public:
-    InputDriver(const char* name);
+    explicit InputDriver(const char* name);
     virtual ~InputDriver() = default;
 
     virtual void init() override;
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
index 859c3b8..f1d3726 100644
--- a/services/inputflinger/host/InputFlinger.cpp
+++ b/services/inputflinger/host/InputFlinger.cpp
@@ -16,21 +16,19 @@
 
 #define LOG_TAG "InputFlinger"
 
-
 #include <stdint.h>
-#include <unistd.h>
-
 #include <sys/types.h>
-
-#include "InputFlinger.h"
-#include "InputDriver.h"
+#include <unistd.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/PermissionCache.h>
 #include <hardware/input.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "InputFlinger.h"
+#include "InputDriver.h"
+
 namespace android {
 
 const String16 sAccessInputFlingerPermission("android.permission.ACCESS_INPUT_FLINGER");
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a7fe69c..dcfe114 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -29,6 +29,10 @@
 static const int32_t DISPLAY_ID = 0;
 static const int32_t DISPLAY_WIDTH = 480;
 static const int32_t DISPLAY_HEIGHT = 800;
+static const int32_t VIRTUAL_DISPLAY_ID = 1;
+static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID";
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -136,25 +140,19 @@
     FakeInputReaderPolicy() {
     }
 
-    void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) {
+    void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const String8& uniqueId) {
+        DisplayViewport v = createDisplayViewport(displayId, width, height, orientation, uniqueId);
         // Set the size of both the internal and external display at the same time.
-        bool isRotated = (orientation == DISPLAY_ORIENTATION_90
-                || orientation == DISPLAY_ORIENTATION_270);
-        DisplayViewport v;
-        v.displayId = displayId;
-        v.orientation = orientation;
-        v.logicalLeft = 0;
-        v.logicalTop = 0;
-        v.logicalRight = isRotated ? height : width;
-        v.logicalBottom = isRotated ? width : height;
-        v.physicalLeft = 0;
-        v.physicalTop = 0;
-        v.physicalRight = isRotated ? height : width;
-        v.physicalBottom = isRotated ? width : height;
-        v.deviceWidth = isRotated ? height : width;
-        v.deviceHeight = isRotated ? width : height;
-        mConfig.setDisplayInfo(false /*external*/, v);
-        mConfig.setDisplayInfo(true /*external*/, v);
+        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, v);
+        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, v);
+    }
+
+    void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const String8& uniqueId) {
+        Vector<DisplayViewport> viewports;
+        viewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId));
+        mConfig.setVirtualDisplayViewports(viewports);
     }
 
     void addExcludedDeviceName(const String8& deviceName) {
@@ -182,7 +180,32 @@
         transform = t;
     }
 
+    void setPointerCapture(bool enabled) {
+        mConfig.pointerCapture = enabled;
+    }
+
 private:
+    DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+            int32_t orientation, const String8& uniqueId) {
+        bool isRotated = (orientation == DISPLAY_ORIENTATION_90
+                || orientation == DISPLAY_ORIENTATION_270);
+        DisplayViewport v;
+        v.displayId = displayId;
+        v.orientation = orientation;
+        v.logicalLeft = 0;
+        v.logicalTop = 0;
+        v.logicalRight = isRotated ? height : width;
+        v.logicalBottom = isRotated ? width : height;
+        v.physicalLeft = 0;
+        v.physicalTop = 0;
+        v.physicalRight = isRotated ? height : width;
+        v.physicalBottom = isRotated ? width : height;
+        v.deviceWidth = isRotated ? height : width;
+        v.deviceHeight = isRotated ? width : height;
+        v.uniqueId = uniqueId;
+        return v;
+    }
+
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
         *outConfig = mConfig;
     }
@@ -325,7 +348,7 @@
         KeyedVector<int32_t, bool> leds;
         Vector<VirtualKeyDefinition> virtualKeys;
 
-        Device(uint32_t classes) :
+        explicit Device(uint32_t classes) :
                 classes(classes) {
         }
     };
@@ -744,6 +767,10 @@
         mGlobalMetaState = state;
     }
 
+    uint32_t getGeneration() {
+        return mGeneration;
+    }
+
 private:
     virtual void updateGlobalMetaState() {
         mUpdateGlobalMetaStateWasCalled = true;
@@ -1425,17 +1452,26 @@
         mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
     }
 
+    void configureDevice(uint32_t changes) {
+        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+    }
+
     void addMapperAndConfigure(InputMapper* mapper) {
         mDevice->addMapper(mapper);
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+        configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
             int32_t orientation) {
-        mFakePolicy->setDisplayInfo(displayId, width, height, orientation);
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+        mFakePolicy->setDisplayViewport(displayId, width, height, orientation, String8::empty());
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+            int32_t orientation, const String8& uniqueId) {
+        mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId);
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
     static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
@@ -2078,6 +2114,25 @@
     ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+    ASSERT_EQ(DEVICE_ID, args.deviceId);
+    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+    ASSERT_EQ(0, args.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
+    ASSERT_EQ(0, args.edgeFlags);
+    ASSERT_EQ(uint32_t(1), args.pointerCount);
+    ASSERT_EQ(0, args.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+    ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+    ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
     // Button release.  Should have same down time.
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
@@ -2086,6 +2141,25 @@
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
     ASSERT_EQ(uint32_t(0), args.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+    ASSERT_EQ(0, args.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+    ASSERT_EQ(0, args.buttonState);
+    ASSERT_EQ(0, args.edgeFlags);
+    ASSERT_EQ(uint32_t(1), args.pointerCount);
+    ASSERT_EQ(0, args.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+    ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+    ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+    ASSERT_EQ(DEVICE_ID, args.deviceId);
+    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(0, args.flags);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -2140,10 +2214,20 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     // Button release.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2167,6 +2251,12 @@
             1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
             1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+            1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     // Move X, Y a bit while pressed.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
@@ -2181,6 +2271,11 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2277,19 +2372,33 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
@@ -2306,23 +2415,57 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
@@ -2336,19 +2479,35 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
@@ -2361,21 +2520,37 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2386,21 +2561,37 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2411,21 +2602,37 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2446,6 +2653,96 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperTest, Process_PointerCapture) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "pointer");
+    mFakePolicy->setPointerCapture(true);
+    addMapperAndConfigure(mapper);
+
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
+
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    NotifyMotionArgs args;
+
+    // Move.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+
+    // Button press.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    // Button release.
+    process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    // Another move.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 30);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 40);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+
+    // Disable pointer capture and check that the device generation got bumped
+    // and events are generated the usual way.
+    const uint32_t generation = mFakeContext->getGeneration();
+    mFakePolicy->setPointerCapture(false);
+    configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+    ASSERT_TRUE(mFakeContext->getGeneration() != generation);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
@@ -2479,6 +2776,8 @@
     static const int32_t RAW_SLOT_MAX;
     static const float X_PRECISION;
     static const float Y_PRECISION;
+    static const float X_PRECISION_VIRTUAL;
+    static const float Y_PRECISION_VIRTUAL;
 
     static const float GEOMETRIC_SCALE;
     static const TouchAffineTransformation AFFINE_TRANSFORM;
@@ -2500,6 +2799,7 @@
     };
 
     void prepareDisplay(int32_t orientation);
+    void prepareVirtualDisplay(int32_t orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
@@ -2507,7 +2807,10 @@
     float toCookedX(float rawX, float rawY);
     float toCookedY(float rawX, float rawY);
     float toDisplayX(int32_t rawX);
+    float toDisplayX(int32_t rawX, int32_t displayWidth);
     float toDisplayY(int32_t rawY);
+    float toDisplayY(int32_t rawY, int32_t displayHeight);
+
 };
 
 const int32_t TouchInputMapperTest::RAW_X_MIN = 25;
@@ -2532,6 +2835,10 @@
 const int32_t TouchInputMapperTest::RAW_SLOT_MAX = 9;
 const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
 const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
+const float TouchInputMapperTest::X_PRECISION_VIRTUAL =
+        float(RAW_X_MAX - RAW_X_MIN + 1) / VIRTUAL_DISPLAY_WIDTH;
+const float TouchInputMapperTest::Y_PRECISION_VIRTUAL =
+        float(RAW_Y_MAX - RAW_Y_MIN + 1) / VIRTUAL_DISPLAY_HEIGHT;
 const TouchAffineTransformation TouchInputMapperTest::AFFINE_TRANSFORM =
         TouchAffineTransformation(1, -2, 3, -4, 5, -6);
 
@@ -2548,6 +2855,11 @@
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
 }
 
+void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+    setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+        VIRTUAL_DISPLAY_HEIGHT, orientation, String8(VIRTUAL_DISPLAY_UNIQUE_ID));
+}
+
 void TouchInputMapperTest::prepareVirtualKeys() {
     mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
     mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
@@ -2578,11 +2890,19 @@
 }
 
 float TouchInputMapperTest::toDisplayX(int32_t rawX) {
-    return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN + 1);
+    return toDisplayX(rawX, DISPLAY_WIDTH);
+}
+
+float TouchInputMapperTest::toDisplayX(int32_t rawX, int32_t displayWidth) {
+    return float(rawX - RAW_X_MIN) * displayWidth / (RAW_X_MAX - RAW_X_MIN + 1);
 }
 
 float TouchInputMapperTest::toDisplayY(int32_t rawY) {
-    return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN + 1);
+    return toDisplayY(rawY, DISPLAY_HEIGHT);
+}
+
+float TouchInputMapperTest::toDisplayY(int32_t rawY, int32_t displayHeight) {
+    return float(rawY - RAW_Y_MIN) * displayHeight / (RAW_Y_MAX - RAW_Y_MIN + 1);
 }
 
 
@@ -3042,6 +3362,105 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
+
+    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    prepareVirtualKeys();
+    addMapperAndConfigure(mapper);
+
+    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+    NotifyMotionArgs motionArgs;
+
+    // Down.
+    int32_t x = 100;
+    int32_t y = 125;
+    processDown(mapper, x, y);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Move.
+    x += 50;
+    y += 75;
+    processMove(mapper, x, y);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Up.
+    processUp(mapper);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Should not have sent any more keys or motions.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -3312,11 +3731,19 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
     processKey(mapper, BTN_LEFT, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
     processKey(mapper, BTN_RIGHT, 1);
@@ -3327,17 +3754,34 @@
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
             motionArgs.buttonState);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+
     processKey(mapper, BTN_RIGHT, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_MIDDLE, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_BACK, release BTN_BACK
     processKey(mapper, BTN_BACK, 1);
@@ -3345,15 +3789,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
 
     processKey(mapper, BTN_BACK, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3364,15 +3818,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
 
     processKey(mapper, BTN_SIDE, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -3383,15 +3847,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
 
     processKey(mapper, BTN_FORWARD, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -3402,44 +3876,72 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
 
     processKey(mapper, BTN_EXTRA, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
     // press BTN_STYLUS, release BTN_STYLUS
     processKey(mapper, BTN_STYLUS, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_STYLUS, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_STYLUS2, release BTN_STYLUS2
     processKey(mapper, BTN_STYLUS2, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_STYLUS2, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // release touch
     processUp(mapper);
@@ -4725,11 +5227,19 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
     processKey(mapper, BTN_LEFT, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
     processKey(mapper, BTN_RIGHT, 1);
@@ -4740,17 +5250,34 @@
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
             motionArgs.buttonState);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+
     processKey(mapper, BTN_RIGHT, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_MIDDLE, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_BACK, release BTN_BACK
     processKey(mapper, BTN_BACK, 1);
@@ -4758,15 +5285,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
 
     processKey(mapper, BTN_BACK, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4777,15 +5314,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
 
     processKey(mapper, BTN_SIDE, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4796,15 +5343,25 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
 
     processKey(mapper, BTN_FORWARD, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4815,44 +5372,72 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
 
     processKey(mapper, BTN_EXTRA, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
 
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+
     // press BTN_STYLUS, release BTN_STYLUS
     processKey(mapper, BTN_STYLUS, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_STYLUS, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // press BTN_STYLUS2, release BTN_STYLUS2
     processKey(mapper, BTN_STYLUS2, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
-    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, motionArgs.buttonState);
 
     processKey(mapper, BTN_STYLUS2, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
 
     // release touch
     processId(mapper, -1);
diff --git a/services/nativeperms/.clang-format b/services/nativeperms/.clang-format
new file mode 100644
index 0000000..6006e6f
--- /dev/null
+++ b/services/nativeperms/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 80
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
diff --git a/services/nativeperms/Android.mk b/services/nativeperms/Android.mk
new file mode 100644
index 0000000..34ccd0b
--- /dev/null
+++ b/services/nativeperms/Android.mk
@@ -0,0 +1,31 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := nativeperms
+LOCAL_SRC_FILES := \
+    nativeperms.cpp \
+    android/os/IPermissionController.aidl
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SHARED_LIBRARIES := \
+    libbinder \
+    libbrillo \
+    libbrillo-binder \
+    libchrome \
+    libutils
+LOCAL_INIT_RC := nativeperms.rc
+include $(BUILD_EXECUTABLE)
diff --git a/services/nativeperms/android/os/IPermissionController.aidl b/services/nativeperms/android/os/IPermissionController.aidl
new file mode 100644
index 0000000..89db85c
--- /dev/null
+++ b/services/nativeperms/android/os/IPermissionController.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/os/IPowerManager.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.os;
+
+/** @hide */
+interface IPermissionController {
+    boolean checkPermission(String permission, int pid, int uid);
+    String[] getPackagesForUid(int uid);
+    boolean isRuntimePermission(String permission);
+}
diff --git a/services/nativeperms/android/os/README b/services/nativeperms/android/os/README
new file mode 100644
index 0000000..e414499
--- /dev/null
+++ b/services/nativeperms/android/os/README
@@ -0,0 +1,4 @@
+IPermissionController.aidl in this directory is a verbatim copy of
+https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/IPermissionController.aidl,
+because some Brillo manifests do not currently include the frameworks/base repo.
+TODO(jorgelo): Figure out a way to use the .aidl file in frameworks/base.
diff --git a/services/nativeperms/nativeperms.cpp b/services/nativeperms/nativeperms.cpp
new file mode 100644
index 0000000..7f03bed
--- /dev/null
+++ b/services/nativeperms/nativeperms.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/at_exit.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <brillo/binder_watcher.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/syslog_logging.h>
+#include <utils/String16.h>
+
+#include "android/os/BnPermissionController.h"
+
+namespace {
+static android::String16 serviceName("permission");
+}
+
+namespace android {
+
+class PermissionService : public android::os::BnPermissionController {
+   public:
+    ::android::binder::Status checkPermission(
+            const ::android::String16& permission, int32_t pid, int32_t uid,
+            bool* _aidl_return) {
+        (void)permission;
+        (void)pid;
+        (void)uid;
+        *_aidl_return = true;
+        return binder::Status::ok();
+    }
+
+    ::android::binder::Status getPackagesForUid(
+            int32_t uid, ::std::vector<::android::String16>* _aidl_return) {
+        (void)uid;
+        // Brillo doesn't currently have installable packages.
+        if (_aidl_return) {
+            _aidl_return->clear();
+        }
+        return binder::Status::ok();
+    }
+
+    ::android::binder::Status isRuntimePermission(
+            const ::android::String16& permission, bool* _aidl_return) {
+        (void)permission;
+        // Brillo doesn't currently have runtime permissions.
+        *_aidl_return = false;
+        return binder::Status::ok();
+    }
+};
+
+}  // namespace android
+
+int main() {
+    base::AtExitManager atExitManager;
+    brillo::InitLog(brillo::kLogToSyslog);
+    // Register the service with servicemanager.
+    android::status_t status = android::defaultServiceManager()->addService(
+            serviceName, new android::PermissionService());
+    CHECK(status == android::OK) << "Failed to get IPermissionController "
+                                    "binder from servicemanager.";
+
+    // Create a message loop.
+    base::MessageLoopForIO messageLoopForIo;
+    brillo::BaseMessageLoop messageLoop{&messageLoopForIo};
+
+    // Initialize a binder watcher.
+    brillo::BinderWatcher watcher(&messageLoop);
+    watcher.Init();
+
+    // Run the message loop.
+    messageLoop.Run();
+}
diff --git a/services/nativeperms/nativeperms.rc b/services/nativeperms/nativeperms.rc
new file mode 100644
index 0000000..704c0a2
--- /dev/null
+++ b/services/nativeperms/nativeperms.rc
@@ -0,0 +1,4 @@
+service nativeperms /system/bin/nativeperms
+    class main
+    user system
+    group system
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
new file mode 100644
index 0000000..7b3af70
--- /dev/null
+++ b/services/powermanager/Android.bp
@@ -0,0 +1,17 @@
+cc_library_shared {
+    name: "libpowermanager",
+
+    srcs: ["IPowerManager.cpp"],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/powermanager/Android.mk b/services/powermanager/Android.mk
deleted file mode 100644
index 4deb115..0000000
--- a/services/powermanager/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    IPowerManager.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libutils \
-    libbinder
-
-LOCAL_MODULE:= libpowermanager
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../../include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
index bff8719..ea3a831 100644
--- a/services/powermanager/IPowerManager.cpp
+++ b/services/powermanager/IPowerManager.cpp
@@ -30,7 +30,7 @@
 class BpPowerManager : public BpInterface<IPowerManager>
 {
 public:
-    BpPowerManager(const sp<IBinder>& impl)
+    explicit BpPowerManager(const sp<IBinder>& impl)
         : BpInterface<IPowerManager>(impl)
     {
     }
diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp
new file mode 100644
index 0000000..1f1340c
--- /dev/null
+++ b/services/schedulerservice/Android.bp
@@ -0,0 +1,25 @@
+cc_library_shared {
+    name: "libschedulerservicehidl",
+    srcs: [
+        "SchedulingPolicyService.cpp",
+    ],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libmediautils",
+        "liblog",
+        "libutils",
+        "android.hidl.base@1.0",
+        "android.frameworks.schedulerservice@1.0",
+    ],
+    header_libs: [
+        "libcutils_headers",
+    ],
+    export_include_dirs: [
+        "include/"
+    ],
+    local_include_dirs: [
+        "include/schedulerservice/",
+    ],
+}
diff --git a/services/schedulerservice/SchedulingPolicyService.cpp b/services/schedulerservice/SchedulingPolicyService.cpp
new file mode 100644
index 0000000..1f6ed57
--- /dev/null
+++ b/services/schedulerservice/SchedulingPolicyService.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "schedulerservicehidl"
+
+#include "SchedulingPolicyService.h"
+
+#include <log/log.h>
+#include <hwbinder/IPCThreadState.h>
+#include <mediautils/SchedulingPolicyService.h>
+
+namespace android {
+namespace frameworks {
+namespace schedulerservice {
+namespace V1_0 {
+namespace implementation {
+
+bool SchedulingPolicyService::isAllowed() {
+    // TODO(b/37291237)
+    return true;
+}
+
+Return<bool> SchedulingPolicyService::requestPriority(int32_t pid, int32_t tid, int32_t priority) {
+    if (priority < static_cast<int32_t>(Priority::MIN) ||
+            priority > static_cast<int32_t>(Priority::MAX)) {
+        return false;
+    }
+
+    if (!isAllowed()) {
+        return false;
+    }
+
+    // TODO(b/37226359): decouple from and remove AIDL service
+    // this should always be allowed since we are in system_server.
+    int value = ::android::requestPriority(pid, tid, priority, false /* isForApp */);
+    return value == 0 /* success */;
+}
+
+Return<int32_t> SchedulingPolicyService::getMaxAllowedPriority() {
+    if (!isAllowed()) {
+        return 0;
+    }
+
+    // TODO(b/37226359): decouple from and remove AIDL service
+    return 3;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace schedulerservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/schedulerservice/include/schedulerservice/SchedulingPolicyService.h b/services/schedulerservice/include/schedulerservice/SchedulingPolicyService.h
new file mode 100644
index 0000000..7d1c478
--- /dev/null
+++ b/services/schedulerservice/include/schedulerservice/SchedulingPolicyService.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/frameworks/schedulerservice/1.0/ISchedulingPolicyService.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace frameworks {
+namespace schedulerservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::frameworks::schedulerservice::V1_0::ISchedulingPolicyService;
+using ::android::hidl::base::V1_0::DebugInfo;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct SchedulingPolicyService : public ISchedulingPolicyService {
+    Return<bool> requestPriority(int32_t pid, int32_t tid, int32_t priority) override;
+    Return<int32_t> getMaxAllowedPriority() override;
+private:
+    bool isAllowed();
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace schedulerservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
new file mode 100644
index 0000000..8c2300e
--- /dev/null
+++ b/services/sensorservice/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+    "hidl"
+]
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 7b10319..cfb7231 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -11,6 +11,7 @@
     RecentEventLogger.cpp \
     RotationVectorSensor.cpp \
     SensorDevice.cpp \
+    SensorDirectConnection.cpp \
     SensorEventConnection.cpp \
     SensorFusion.cpp \
     SensorInterface.cpp \
@@ -19,7 +20,6 @@
     SensorService.cpp \
     SensorServiceUtils.cpp \
 
-
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
 LOCAL_CFLAGS += -Wall -Werror -Wextra
@@ -33,9 +33,20 @@
     libutils \
     liblog \
     libbinder \
-    libui \
-    libgui \
-    libcrypto
+    libsensor \
+    libcrypto \
+    libbase \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    android.hardware.sensors@1.0
+
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.sensors@1.0-convert
+
+# our public headers depend on libsensor
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+    libsensor \
 
 LOCAL_MODULE:= libsensorservice
 
diff --git a/services/sensorservice/CorrectedGyroSensor.h b/services/sensorservice/CorrectedGyroSensor.h
index 68acd43..1d49e01 100644
--- a/services/sensorservice/CorrectedGyroSensor.h
+++ b/services/sensorservice/CorrectedGyroSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h
index 8e33a73..483f468 100644
--- a/services/sensorservice/GravitySensor.h
+++ b/services/sensorservice/GravitySensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h
index 428baa6..aa4e54a 100644
--- a/services/sensorservice/LinearAccelerationSensor.h
+++ b/services/sensorservice/LinearAccelerationSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 #include "GravitySensor.h"
diff --git a/services/sensorservice/OrientationSensor.h b/services/sensorservice/OrientationSensor.h
index 30ff226..a3f2a99 100644
--- a/services/sensorservice/OrientationSensor.h
+++ b/services/sensorservice/OrientationSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index 8b15e5a..bf1f655 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -35,7 +35,7 @@
 // behavior.
 class RecentEventLogger : public Dumpable {
 public:
-    RecentEventLogger(int sensorType);
+    explicit RecentEventLogger(int sensorType);
     void addEvent(const sensors_event_t& event);
     bool populateLastEvent(sensors_event_t *event) const;
     bool isEmpty() const;
@@ -47,7 +47,7 @@
 
 protected:
     struct SensorEventLog {
-        SensorEventLog(const sensors_event_t& e);
+        explicit SensorEventLog(const sensors_event_t& e);
         timespec mWallTime;
         sensors_event_t mEvent;
     };
diff --git a/services/sensorservice/RingBuffer.h b/services/sensorservice/RingBuffer.h
index ec98a01..a60eb90 100644
--- a/services/sensorservice/RingBuffer.h
+++ b/services/sensorservice/RingBuffer.h
@@ -39,7 +39,7 @@
     /**
      * Construct a RingBuffer that can grow up to the given length.
      */
-    RingBuffer(size_t length);
+    explicit RingBuffer(size_t length);
 
     /**
      * Forward iterator to this class.  Implements an std:forward_iterator.
diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
index 3cc2248..34deaa9 100644
--- a/services/sensorservice/RotationVectorSensor.h
+++ b/services/sensorservice/RotationVectorSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorDevice.h"
 #include "SensorInterface.h"
@@ -34,7 +34,7 @@
 
 class RotationVectorSensor : public VirtualSensor {
 public:
-    RotationVectorSensor(int mode = FUSION_9AXIS);
+    explicit RotationVectorSensor(int mode = FUSION_9AXIS);
     virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
     virtual status_t activate(void* ident, bool enabled) override;
     virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index ac03742..7d9b0b7 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -13,133 +13,213 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "SensorDevice.h"
+#include "SensorService.h"
 
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android-base/logging.h>
+#include <sensors/convert.h>
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
-#include <binder/BinderService.h>
-#include <binder/Parcel.h>
-#include <binder/IServiceManager.h>
+#include <chrono>
+#include <cinttypes>
+#include <thread>
 
-#include <hardware/sensors.h>
+using android::hardware::hidl_vec;
 
-#include "SensorDevice.h"
-#include "SensorService.h"
+using namespace android::hardware::sensors::V1_0;
+using namespace android::hardware::sensors::V1_0::implementation;
+
 
 namespace android {
 // ---------------------------------------------------------------------------
 
 ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
 
-SensorDevice::SensorDevice()
-    :  mSensorDevice(0),
-       mSensorModule(0) {
-    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-            (hw_module_t const**)&mSensorModule);
-
-    ALOGE_IF(err, "couldn't load %s module (%s)",
-            SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-    if (mSensorModule) {
-        err = sensors_open_1(&mSensorModule->common, &mSensorDevice);
-
-        ALOGE_IF(err, "couldn't open device for module %s (%s)",
-                SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-        if (mSensorDevice) {
-            if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
-                mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
-                ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
-            }
-
-            sensor_t const* list;
-            ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
-            mActivationCount.setCapacity(count);
-            Info model;
-            for (size_t i=0 ; i<size_t(count) ; i++) {
-                mActivationCount.add(list[i].handle, model);
-                mSensorDevice->activate(
-                        reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                        list[i].handle, 0);
-            }
-        }
+static status_t StatusFromResult(Result result) {
+    switch (result) {
+        case Result::OK:
+            return OK;
+        case Result::BAD_VALUE:
+            return BAD_VALUE;
+        case Result::PERMISSION_DENIED:
+            return PERMISSION_DENIED;
+        case Result::INVALID_OPERATION:
+            return INVALID_OPERATION;
+        case Result::NO_MEMORY:
+            return NO_MEMORY;
     }
 }
 
+SensorDevice::SensorDevice() : mHidlTransportErrors(20) {
+    if (!connectHidlService()) {
+        return;
+    }
+
+    float minPowerMa = 0.001; // 1 microAmp
+
+    checkReturn(mSensors->getSensorsList(
+            [&](const auto &list) {
+                const size_t count = list.size();
+
+                mActivationCount.setCapacity(count);
+                Info model;
+                for (size_t i=0 ; i < count; i++) {
+                    sensor_t sensor;
+                    convertToSensor(list[i], &sensor);
+                    // Sanity check and clamp power if it is 0 (or close)
+                    if (sensor.power < minPowerMa) {
+                        ALOGE("Reported power %f not deemed sane, clamping to %f",
+                              sensor.power, minPowerMa);
+                        sensor.power = minPowerMa;
+                    }
+                    mSensorList.push_back(sensor);
+
+                    mActivationCount.add(list[i].sensorHandle, model);
+
+                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                }
+            }));
+
+    mIsDirectReportSupported =
+           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+}
+
+bool SensorDevice::connectHidlService() {
+    // SensorDevice may wait upto 100ms * 10 = 1s for hidl service.
+    constexpr auto RETRY_DELAY = std::chrono::milliseconds(100);
+    size_t retry = 10;
+
+    while (true) {
+        int initStep = 0;
+        mSensors = ISensors::getService();
+        if (mSensors != nullptr) {
+            ++initStep;
+            // Poke ISensor service. If it has lingering connection from previous generation of
+            // system server, it will kill itself. There is no intention to handle the poll result,
+            // which will be done since the size is 0.
+            if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
+                // ok to continue
+                break;
+            }
+            // hidl service is restarting, pointer is invalid.
+            mSensors = nullptr;
+        }
+
+        if (--retry <= 0) {
+            ALOGE("Cannot connect to ISensors hidl service!");
+            break;
+        }
+        // Delay 100ms before retry, hidl service is expected to come up in short time after
+        // crash.
+        ALOGI("%s unsuccessful, try again soon (remaining retry %zu).",
+                (initStep == 0) ? "getService()" : "poll() check", retry);
+        std::this_thread::sleep_for(RETRY_DELAY);
+    }
+    return (mSensors != nullptr);
+}
+
 void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
+    // not need to check mSensors because this is is only called after successful poll()
     if (connected) {
         Info model;
         mActivationCount.add(handle, model);
-        mSensorDevice->activate(
-                reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice), handle, 0);
+        checkReturn(mSensors->activate(handle, 0 /* enabled */));
     } else {
         mActivationCount.removeItem(handle);
     }
 }
 
 std::string SensorDevice::dump() const {
-    if (!mSensorModule) return "HAL not initialized\n";
+    if (mSensors == nullptr) return "HAL not initialized\n";
 
     String8 result;
-    sensor_t const* list;
-    int count = mSensorModule->get_sensors_list(mSensorModule, &list);
-
-    result.appendFormat("HAL: %s (%s), version %#010x\n",
-                        mSensorModule->common.name,
-                        mSensorModule->common.author,
-                        getHalDeviceVersion());
-    result.appendFormat("Total %d h/w sensors, %zu running:\n", count, mActivationCount.size());
+    result.appendFormat("Total %zu h/w sensors, %zu running:\n",
+                        mSensorList.size(), mActivationCount.size());
 
     Mutex::Autolock _l(mLock);
-    for (int i = 0 ; i < count ; i++) {
-        const Info& info = mActivationCount.valueFor(list[i].handle);
+    for (const auto & s : mSensorList) {
+        int32_t handle = s.handle;
+        const Info& info = mActivationCount.valueFor(handle);
         if (info.batchParams.isEmpty()) continue;
-        result.appendFormat("0x%08x) active-count = %zu; ", list[i].handle,
-                            info.batchParams.size());
+
+        result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
 
         result.append("sampling_period(ms) = {");
         for (size_t j = 0; j < info.batchParams.size(); j++) {
-            const BatchParams& params = info.batchParams.valueAt(j);
-            result.appendFormat("%.1f%s", params.batchDelay / 1e6f,
-                                j < info.batchParams.size() - 1 ? ", " : "");
+            const BatchParams& params = info.batchParams[j];
+            result.appendFormat("%.1f%s", params.mTSample / 1e6f,
+                j < info.batchParams.size() - 1 ? ", " : "");
         }
-        result.appendFormat("}, selected = %.1f ms; ", info.bestBatchParams.batchDelay / 1e6f);
+        result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f);
 
         result.append("batching_period(ms) = {");
         for (size_t j = 0; j < info.batchParams.size(); j++) {
-            BatchParams params = info.batchParams.valueAt(j);
-            result.appendFormat("%.1f%s", params.batchTimeout / 1e6f,
-                                j < info.batchParams.size() - 1 ? ", " : "");
+            const BatchParams& params = info.batchParams[j];
+            result.appendFormat("%.1f%s", params.mTBatch / 1e6f,
+                    j < info.batchParams.size() - 1 ? ", " : "");
         }
-        result.appendFormat("}, selected = %.1f ms\n", info.bestBatchParams.batchTimeout / 1e6f);
+        result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
     }
+
     return result.string();
 }
 
 ssize_t SensorDevice::getSensorList(sensor_t const** list) {
-    if (!mSensorModule) return NO_INIT;
-    ssize_t count = mSensorModule->get_sensors_list(mSensorModule, list);
-    return count;
+    *list = &mSensorList[0];
+
+    return mSensorList.size();
 }
 
 status_t SensorDevice::initCheck() const {
-    return mSensorDevice && mSensorModule ? NO_ERROR : NO_INIT;
+    return mSensors != NULL ? NO_ERROR : NO_INIT;
 }
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
-    if (!mSensorDevice) return NO_INIT;
-    ssize_t c;
+    if (mSensors == nullptr) return NO_INIT;
+
+    ssize_t err;
+    int numHidlTransportErrors = 0;
+    bool hidlTransportError = false;
+
     do {
-        c = mSensorDevice->poll(reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice),
-                                buffer, count);
-    } while (c == -EINTR);
-    return c;
+        auto ret = mSensors->poll(
+                count,
+                [&](auto result,
+                    const auto &events,
+                    const auto &dynamicSensorsAdded) {
+                    if (result == Result::OK) {
+                        convertToSensorEvents(events, dynamicSensorsAdded, buffer);
+                        err = (ssize_t)events.size();
+                    } else {
+                        err = StatusFromResult(result);
+                    }
+                });
+
+        if (ret.isOk())  {
+            hidlTransportError = false;
+        } else {
+            hidlTransportError = true;
+            numHidlTransportErrors++;
+            if (numHidlTransportErrors > 50) {
+                // Log error and bail
+                ALOGE("Max Hidl transport errors this cycle : %d", numHidlTransportErrors);
+                handleHidlDeath(ret.description());
+            } else {
+                std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            }
+        }
+    } while (hidlTransportError);
+
+    if(numHidlTransportErrors > 0) {
+        ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
+        HidlTransportErrorLog errLog(time(NULL), numHidlTransportErrors);
+        mHidlTransportErrors.add(errLog);
+        mTotalHidlTransportErrors++;
+    }
+
+    return err;
 }
 
 void SensorDevice::autoDisable(void *ident, int handle) {
@@ -149,7 +229,8 @@
 }
 
 status_t SensorDevice::activate(void* ident, int handle, int enabled) {
-    if (!mSensorDevice) return NO_INIT;
+    if (mSensors == nullptr) return NO_INIT;
+
     status_t err(NO_ERROR);
     bool actuateHardware = false;
 
@@ -164,6 +245,8 @@
         ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
 
         if (isClientDisabledLocked(ident)) {
+            ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+                    ident, handle);
             return INVALID_OPERATION;
         }
 
@@ -179,24 +262,27 @@
     } else {
         ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident));
 
+        // If a connected dynamic sensor is deactivated, remove it from the
+        // dictionary.
+        auto it = mConnectedDynamicSensors.find(handle);
+        if (it != mConnectedDynamicSensors.end()) {
+            delete it->second;
+            mConnectedDynamicSensors.erase(it);
+        }
+
         if (info.removeBatchParamsForIdent(ident) >= 0) {
             if (info.numActiveClients() == 0) {
                 // This is the last connection, we need to de-activate the underlying h/w sensor.
                 actuateHardware = true;
             } else {
-                const int halVersion = getHalDeviceVersion();
-                if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
-                    // Call batch for this sensor with the previously calculated best effort
-                    // batch_rate and timeout. One of the apps has unregistered for sensor
-                    // events, and the best effort batch parameters might have changed.
-                    ALOGD_IF(DEBUG_CONNECTIONS,
-                             "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
-                             info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                             info.bestBatchParams.batchTimeout);
-                    mSensorDevice->batch(mSensorDevice, handle,info.bestBatchParams.flags,
-                                         info.bestBatchParams.batchDelay,
-                                         info.bestBatchParams.batchTimeout);
-                }
+                // Call batch for this sensor with the previously calculated best effort
+                // batch_rate and timeout. One of the apps has unregistered for sensor
+                // events, and the best effort batch parameters might have changed.
+                ALOGD_IF(DEBUG_CONNECTIONS,
+                         "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle,
+                         info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
+                checkReturn(mSensors->batch(
+                        handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
             }
         } else {
             // sensor wasn't enabled for this ident
@@ -210,8 +296,7 @@
     if (actuateHardware) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
                  enabled);
-        err = mSensorDevice->activate(
-                reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice), handle, enabled);
+        err = StatusFromResult(checkReturn(mSensors->activate(handle, enabled)));
         ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
                  strerror(-err));
 
@@ -221,29 +306,22 @@
         }
     }
 
-    // On older devices which do not support batch, call setDelay().
-    if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1 && info.numActiveClients() > 0) {
-        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w setDelay %d %" PRId64, handle,
-                 info.bestBatchParams.batchDelay);
-        mSensorDevice->setDelay(
-                reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                handle, info.bestBatchParams.batchDelay);
-    }
     return err;
 }
 
-status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
-                             int64_t maxBatchReportLatencyNs) {
-    if (!mSensorDevice) return NO_INIT;
+status_t SensorDevice::batch(
+        void* ident,
+        int handle,
+        int flags,
+        int64_t samplingPeriodNs,
+        int64_t maxBatchReportLatencyNs) {
+    if (mSensors == nullptr) return NO_INIT;
 
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
     }
-
-    const int halVersion = getHalDeviceVersion();
-    if (halVersion < SENSORS_DEVICE_API_VERSION_1_1 && maxBatchReportLatencyNs != 0) {
-        // Batch is not supported on older devices return invalid operation.
-        return INVALID_OPERATION;
+    if (maxBatchReportLatencyNs < 0) {
+        maxBatchReportLatencyNs = 0;
     }
 
     ALOGD_IF(DEBUG_CONNECTIONS,
@@ -254,7 +332,7 @@
     Info& info(mActivationCount.editValueFor(handle));
 
     if (info.batchParams.indexOfKey(ident) < 0) {
-        BatchParams params(flags, samplingPeriodNs, maxBatchReportLatencyNs);
+        BatchParams params(samplingPeriodNs, maxBatchReportLatencyNs);
         info.batchParams.add(ident, params);
     } else {
         // A batch has already been called with this ident. Update the batch parameters.
@@ -268,29 +346,21 @@
     ALOGD_IF(DEBUG_CONNECTIONS,
              "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
              " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
-             prevBestBatchParams.batchDelay, info.bestBatchParams.batchDelay,
-             prevBestBatchParams.batchTimeout, info.bestBatchParams.batchTimeout);
+             prevBestBatchParams.mTSample, info.bestBatchParams.mTSample,
+             prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch);
 
     status_t err(NO_ERROR);
     // If the min period or min timeout has changed since the last batch call, call batch.
     if (prevBestBatchParams != info.bestBatchParams) {
-        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
-            ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
-                     info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                     info.bestBatchParams.batchTimeout);
-            err = mSensorDevice->batch(mSensorDevice, handle, info.bestBatchParams.flags,
-                                       info.bestBatchParams.batchDelay,
-                                       info.bestBatchParams.batchTimeout);
-        } else {
-            // For older devices which do not support batch, call setDelay() after activate() is
-            // called. Some older devices may not support calling setDelay before activate(), so
-            // call setDelay in SensorDevice::activate() method.
-        }
+        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
+                 info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
+        err = StatusFromResult(
+                checkReturn(mSensors->batch(
+                    handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)));
         if (err != NO_ERROR) {
-            ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
-                  mSensorDevice, handle,
-                  info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                  info.bestBatchParams.batchTimeout, strerror(-err));
+            ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
+                  mSensors.get(), handle, info.bestBatchParams.mTSample,
+                  info.bestBatchParams.mTBatch, strerror(-err));
             info.removeBatchParamsForIdent(ident);
         }
     }
@@ -298,41 +368,19 @@
 }
 
 status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
-    if (!mSensorDevice) return NO_INIT;
-    if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
-        samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
-    }
-    Mutex::Autolock _l(mLock);
-    if (isClientDisabledLocked(ident)) return INVALID_OPERATION;
-    Info& info( mActivationCount.editValueFor(handle) );
-    // If the underlying sensor is NOT in continuous mode, setDelay() should return an error.
-    // Calling setDelay() in batch mode is an invalid operation.
-    if (info.bestBatchParams.batchTimeout != 0) {
-      return INVALID_OPERATION;
-    }
-    ssize_t index = info.batchParams.indexOfKey(ident);
-    if (index < 0) {
-        return BAD_INDEX;
-    }
-    BatchParams& params = info.batchParams.editValueAt(index);
-    params.batchDelay = samplingPeriodNs;
-    info.selectBatchParams();
-    return mSensorDevice->setDelay(reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                                   handle, info.bestBatchParams.batchDelay);
+    return batch(ident, handle, 0, samplingPeriodNs, 0);
 }
 
 int SensorDevice::getHalDeviceVersion() const {
-    if (!mSensorDevice) return -1;
-    return mSensorDevice->common.version;
+    if (mSensors == nullptr) return -1;
+    return SENSORS_DEVICE_API_VERSION_1_4;
 }
 
 status_t SensorDevice::flush(void* ident, int handle) {
-    if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1) {
-        return INVALID_OPERATION;
-    }
+    if (mSensors == nullptr) return NO_INIT;
     if (isClientDisabled(ident)) return INVALID_OPERATION;
     ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
-    return mSensorDevice->flush(mSensorDevice, handle);
+    return StatusFromResult(checkReturn(mSensors->flush(handle)));
 }
 
 bool SensorDevice::isClientDisabled(void* ident) {
@@ -345,9 +393,10 @@
 }
 
 void SensorDevice::enableAllSensors() {
+    if (mSensors == nullptr) return;
     Mutex::Autolock _l(mLock);
     mDisabledClients.clear();
-    const int halVersion = getHalDeviceVersion();
+    ALOGI("cleared mDisabledClients");
     for (size_t i = 0; i< mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
         if (info.batchParams.isEmpty()) continue;
@@ -355,70 +404,152 @@
         const int sensor_handle = mActivationCount.keyAt(i);
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
                    sensor_handle);
-        status_t err(NO_ERROR);
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
-            err = mSensorDevice->batch(mSensorDevice, sensor_handle,
-                 info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                 info.bestBatchParams.batchTimeout);
-            ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
-        }
+        status_t err = StatusFromResult(
+                checkReturn(mSensors->batch(
+                    sensor_handle,
+                    info.bestBatchParams.mTSample,
+                    info.bestBatchParams.mTBatch)));
+        ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
 
         if (err == NO_ERROR) {
-            err = mSensorDevice->activate(
-                    reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                    sensor_handle, 1);
+            err = StatusFromResult(
+                    checkReturn(mSensors->activate(sensor_handle, 1 /* enabled */)));
             ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
         }
-
-        if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0) {
-             err = mSensorDevice->setDelay(
-                    reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                    sensor_handle, info.bestBatchParams.batchDelay);
-             ALOGE_IF(err, "Error calling setDelay sensor %d (%s)", sensor_handle, strerror(-err));
-        }
     }
 }
 
 void SensorDevice::disableAllSensors() {
+    if (mSensors == nullptr) return;
     Mutex::Autolock _l(mLock);
-   for (size_t i = 0; i< mActivationCount.size(); ++i) {
+    for (size_t i = 0; i< mActivationCount.size(); ++i) {
         const Info& info = mActivationCount.valueAt(i);
         // Check if this sensor has been activated previously and disable it.
         if (info.batchParams.size() > 0) {
            const int sensor_handle = mActivationCount.keyAt(i);
            ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
                    sensor_handle);
-           mSensorDevice->activate(
-                   reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice),
-                   sensor_handle, 0);
+           checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
+
            // Add all the connections that were registered for this sensor to the disabled
            // clients list.
            for (size_t j = 0; j < info.batchParams.size(); ++j) {
                mDisabledClients.add(info.batchParams.keyAt(j));
+               ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
            }
         }
     }
 }
 
-status_t SensorDevice::injectSensorData(const sensors_event_t *injected_sensor_event) {
-      ALOGD_IF(DEBUG_CONNECTIONS,
-              "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
-               injected_sensor_event->sensor,
-               injected_sensor_event->timestamp, injected_sensor_event->data[0],
-               injected_sensor_event->data[1], injected_sensor_event->data[2],
-               injected_sensor_event->data[3], injected_sensor_event->data[4],
-               injected_sensor_event->data[5]);
-      if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
-          return INVALID_OPERATION;
-      }
-      return mSensorDevice->inject_sensor_data(mSensorDevice, injected_sensor_event);
+status_t SensorDevice::injectSensorData(
+        const sensors_event_t *injected_sensor_event) {
+    if (mSensors == nullptr) return NO_INIT;
+    ALOGD_IF(DEBUG_CONNECTIONS,
+            "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+            injected_sensor_event->sensor,
+            injected_sensor_event->timestamp, injected_sensor_event->data[0],
+            injected_sensor_event->data[1], injected_sensor_event->data[2],
+            injected_sensor_event->data[3], injected_sensor_event->data[4],
+            injected_sensor_event->data[5]);
+
+    Event ev;
+    convertFromSensorEvent(*injected_sensor_event, &ev);
+
+    return StatusFromResult(checkReturn(mSensors->injectSensorData(ev)));
 }
 
 status_t SensorDevice::setMode(uint32_t mode) {
-     if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
-          return INVALID_OPERATION;
-     }
-     return mSensorModule->set_operation_mode(mode);
+    if (mSensors == nullptr) return NO_INIT;
+    return StatusFromResult(
+            checkReturn(mSensors->setOperationMode(
+                    static_cast<hardware::sensors::V1_0::OperationMode>(mode))));
+}
+
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+    if (mSensors == nullptr) return NO_INIT;
+    Mutex::Autolock _l(mLock);
+
+    SharedMemType type;
+    switch (memory->type) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            type = SharedMemType::ASHMEM;
+            break;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            type = SharedMemType::GRALLOC;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    SharedMemFormat format;
+    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+        return BAD_VALUE;
+    }
+    format = SharedMemFormat::SENSORS_EVENT;
+
+    SharedMemInfo mem = {
+        .type = type,
+        .format = format,
+        .size = static_cast<uint32_t>(memory->size),
+        .memoryHandle = memory->handle,
+    };
+
+    int32_t ret;
+    checkReturn(mSensors->registerDirectChannel(mem,
+            [&ret](auto result, auto channelHandle) {
+                if (result == Result::OK) {
+                    ret = channelHandle;
+                } else {
+                    ret = StatusFromResult(result);
+                }
+            }));
+    return ret;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+    if (mSensors == nullptr) return;
+    Mutex::Autolock _l(mLock);
+    checkReturn(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
+        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+    if (mSensors == nullptr) return NO_INIT;
+    Mutex::Autolock _l(mLock);
+
+    RateLevel rate;
+    switch(config->rate_level) {
+        case SENSOR_DIRECT_RATE_STOP:
+            rate = RateLevel::STOP;
+            break;
+        case SENSOR_DIRECT_RATE_NORMAL:
+            rate = RateLevel::NORMAL;
+            break;
+        case SENSOR_DIRECT_RATE_FAST:
+            rate = RateLevel::FAST;
+            break;
+        case SENSOR_DIRECT_RATE_VERY_FAST:
+            rate = RateLevel::VERY_FAST;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    int32_t ret;
+    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+            [&ret, rate] (auto result, auto token) {
+                if (rate == RateLevel::STOP) {
+                    ret = StatusFromResult(result);
+                } else {
+                    if (result == Result::OK) {
+                        ret = token;
+                    } else {
+                        ret = StatusFromResult(result);
+                    }
+                }
+            }));
+
+    return ret;
 }
 
 // ---------------------------------------------------------------------------
@@ -434,35 +565,35 @@
     return num;
 }
 
-status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int flags,
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int,
                                                     int64_t samplingPeriodNs,
                                                     int64_t maxBatchReportLatencyNs) {
     ssize_t index = batchParams.indexOfKey(ident);
     if (index < 0) {
-        ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64 ") failed (%s)",
+        ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64
+              " timeout=%" PRId64 ") failed (%s)",
               ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
         return BAD_INDEX;
     }
     BatchParams& params = batchParams.editValueAt(index);
-    params.flags = flags;
-    params.batchDelay = samplingPeriodNs;
-    params.batchTimeout = maxBatchReportLatencyNs;
+    params.mTSample = samplingPeriodNs;
+    params.mTBatch = maxBatchReportLatencyNs;
     return NO_ERROR;
 }
 
 void SensorDevice::Info::selectBatchParams() {
-    BatchParams bestParams(0, -1, -1);
+    BatchParams bestParams; // default to max Tsample and max Tbatch
     SensorDevice& device(SensorDevice::getInstance());
 
     for (size_t i = 0; i < batchParams.size(); ++i) {
-        if (device.isClientDisabledLocked(batchParams.keyAt(i))) continue;
-        BatchParams params = batchParams.valueAt(i);
-        if (bestParams.batchDelay == -1 || params.batchDelay < bestParams.batchDelay) {
-            bestParams.batchDelay = params.batchDelay;
+        if (device.isClientDisabledLocked(batchParams.keyAt(i))) {
+            continue;
         }
-        if (bestParams.batchTimeout == -1 || params.batchTimeout < bestParams.batchTimeout) {
-            bestParams.batchTimeout = params.batchTimeout;
-        }
+        bestParams.merge(batchParams[i]);
+    }
+    // if mTBatch <= mTSample, it is in streaming mode. set mTbatch to 0 to demand this explicitly.
+    if (bestParams.mTBatch <= bestParams.mTSample) {
+        bestParams.mTBatch = 0;
     }
     bestBatchParams = bestParams;
 }
@@ -480,6 +611,61 @@
     mDisabledClients.remove(ident);
 }
 
+bool SensorDevice::isDirectReportSupported() const {
+    return mIsDirectReportSupported;
+}
+
+void SensorDevice::convertToSensorEvent(
+        const Event &src, sensors_event_t *dst) {
+    ::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
+            src, dst);
+
+    if (src.sensorType == SensorType::DYNAMIC_SENSOR_META) {
+        const DynamicSensorInfo &dyn = src.u.dynamic;
+
+        dst->dynamic_sensor_meta.connected = dyn.connected;
+        dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
+        if (dyn.connected) {
+            auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+            CHECK(it != mConnectedDynamicSensors.end());
+
+            dst->dynamic_sensor_meta.sensor = it->second;
+
+            memcpy(dst->dynamic_sensor_meta.uuid,
+                   dyn.uuid.data(),
+                   sizeof(dst->dynamic_sensor_meta.uuid));
+        }
+    }
+}
+
+void SensorDevice::convertToSensorEvents(
+        const hidl_vec<Event> &src,
+        const hidl_vec<SensorInfo> &dynamicSensorsAdded,
+        sensors_event_t *dst) {
+    // Allocate a sensor_t structure for each dynamic sensor added and insert
+    // it into the dictionary of connected dynamic sensors keyed by handle.
+    for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+        const SensorInfo &info = dynamicSensorsAdded[i];
+
+        auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+        CHECK(it == mConnectedDynamicSensors.end());
+
+        sensor_t *sensor = new sensor_t;
+        convertToSensor(info, sensor);
+
+        mConnectedDynamicSensors.insert(
+                std::make_pair(sensor->handle, sensor));
+    }
+
+    for (size_t i = 0; i < src.size(); ++i) {
+        convertToSensorEvent(src[i], &dst[i]);
+    }
+}
+
+void SensorDevice::handleHidlDeath(const std::string & detail) {
+    // restart is the only option at present.
+    LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
-
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d340da3..fd6cee6 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -19,27 +19,66 @@
 
 #include "SensorServiceUtils.h"
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
+#include <stdint.h>
+#include <sys/types.h>
 #include <utils/KeyedVector.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 
-#include <stdint.h>
-#include <sys/types.h>
+#include <string>
+#include <unordered_map>
+#include <algorithm> //std::max std::min
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+
+#include "RingBuffer.h"
 
 // ---------------------------------------------------------------------------
 
 namespace android {
+
 // ---------------------------------------------------------------------------
 using SensorServiceUtil::Dumpable;
+using hardware::Return;
 
 class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
 public:
+
+    class HidlTransportErrorLog {
+     public:
+
+        HidlTransportErrorLog() {
+            mTs = 0;
+            mCount = 0;
+        }
+
+        HidlTransportErrorLog(time_t ts, int count) {
+            mTs = ts;
+            mCount = count;
+        }
+
+        String8 toString() const {
+            String8 result;
+            struct tm *timeInfo = localtime(&mTs);
+            result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
+                                timeInfo->tm_sec, mCount);
+            return result;
+        }
+
+    private:
+        time_t mTs; // timestamp of the error
+        int mCount;   // number of transport errors observed
+    };
+
     ssize_t getSensorList(sensor_t const** list);
+
     void handleDynamicSensorConnection(int handle, bool connected);
     status_t initCheck() const;
     int getHalDeviceVersion() const;
+
     ssize_t poll(sensors_event_t* buffer, size_t count);
+
     status_t activate(void* ident, int handle, int enabled);
     status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
                    int64_t maxBatchReportLatencyNs);
@@ -47,9 +86,17 @@
     status_t setDelay(void* ident, int handle, int64_t ns);
     status_t flush(void* ident, int handle);
     status_t setMode(uint32_t mode);
+
+    bool isDirectReportSupported() const;
+    int32_t registerDirectChannel(const sensors_direct_mem_t *memory);
+    void unregisterDirectChannel(int32_t channelHandle);
+    int32_t configureDirectChannel(int32_t sensorHandle,
+            int32_t channelHandle, const struct sensors_direct_cfg_t *config);
+
     void disableAllSensors();
     void enableAllSensors();
     void autoDisable(void *ident, int handle);
+
     status_t injectSensorData(const sensors_event_t *event);
     void notifyConnectionDestroyed(void *ident);
 
@@ -57,8 +104,11 @@
     virtual std::string dump() const;
 private:
     friend class Singleton<SensorDevice>;
-    sensors_poll_device_1_t* mSensorDevice;
-    struct sensors_module_t* mSensorModule;
+
+    sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+    Vector<sensor_t> mSensorList;
+    std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
+
     static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
     mutable Mutex mLock; // protect mActivationCount[].batchParams
     // fixed-size array after construction
@@ -66,15 +116,18 @@
     // Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from
     // batch call. For continous mode clients, maxBatchReportLatency is set to zero.
     struct BatchParams {
-      // TODO: Get rid of flags parameter everywhere.
-      int flags;
-      nsecs_t batchDelay, batchTimeout;
-      BatchParams() : flags(0), batchDelay(0), batchTimeout(0) {}
-      BatchParams(int flag, nsecs_t delay, nsecs_t timeout): flags(flag), batchDelay(delay),
-          batchTimeout(timeout) { }
+      nsecs_t mTSample, mTBatch;
+      BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
+      BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {}
       bool operator != (const BatchParams& other) {
-          return other.batchDelay != batchDelay || other.batchTimeout != batchTimeout ||
-                 other.flags != flags;
+          return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
+      }
+      // Merge another parameter with this one. The updated mTSample will be the min of the two.
+      // The update mTBatch will be the min of original mTBatch and the apparent batch period
+      // of the other. the apparent batch is the maximum of mTBatch and mTSample,
+      void merge(const BatchParams &other) {
+          mTSample = std::min(mTSample, other.mTSample);
+          mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
       }
     };
 
@@ -90,7 +143,6 @@
         // requested by the client.
         KeyedVector<void*, BatchParams> batchParams;
 
-        Info() : bestBatchParams(0, -1, -1) {}
         // Sets batch parameters for this ident. Returns error if this ident is not already present
         // in the KeyedVector above.
         status_t setBatchParamsForIdent(void* ident, int flags, int64_t samplingPeriodNs,
@@ -105,12 +157,38 @@
     };
     DefaultKeyedVector<int, Info> mActivationCount;
 
+    // Keep track of any hidl transport failures
+    SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
+    int mTotalHidlTransportErrors;
+
     // Use this vector to determine which client is activated or deactivated.
     SortedVector<void *> mDisabledClients;
     SensorDevice();
+    bool connectHidlService();
+
+    static void handleHidlDeath(const std::string &detail);
+    template<typename T>
+    static Return<T> checkReturn(Return<T> &&ret) {
+        if (!ret.isOk()) {
+            handleHidlDeath(ret.description());
+        }
+        return std::move(ret);
+    }
 
     bool isClientDisabled(void* ident);
     bool isClientDisabledLocked(void* ident);
+
+    using Event = hardware::sensors::V1_0::Event;
+    using SensorInfo = hardware::sensors::V1_0::SensorInfo;
+
+    void convertToSensorEvent(const Event &src, sensors_event_t *dst);
+
+    void convertToSensorEvents(
+            const hardware::hidl_vec<Event> &src,
+            const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
+            sensors_event_t *dst);
+
+    bool mIsDirectReportSupported;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
new file mode 100644
index 0000000..870635b
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SensorDevice.h"
+#include "SensorDirectConnection.h"
+#include <hardware/sensors.h>
+
+#define UNUSED(x) (void)(x)
+
+namespace android {
+
+SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
+        uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
+        const String16& opPackageName)
+        : mService(service), mUid(uid), mMem(*mem),
+        mHalChannelHandle(halChannelHandle),
+        mOpPackageName(opPackageName) {
+    ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
+}
+
+SensorService::SensorDirectConnection::~SensorDirectConnection() {
+    ALOGD_IF(DEBUG_CONNECTIONS, "~SensorDirectConnection %p", this);
+
+    stopAll();
+    mService->cleanupConnection(this);
+    if (mMem.handle != nullptr) {
+        native_handle_close(mMem.handle);
+        native_handle_delete(const_cast<struct native_handle*>(mMem.handle));
+    }
+}
+
+void SensorService::SensorDirectConnection::onFirstRef() {
+}
+
+void SensorService::SensorDirectConnection::dump(String8& result) const {
+    Mutex::Autolock _l(mConnectionLock);
+    result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
+            String8(mOpPackageName).string(), getHalChannelHandle(), mActivated.size());
+    for (auto &i : mActivated) {
+        result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+    }
+}
+
+sp<BitTube> SensorService::SensorDirectConnection::getSensorChannel() const {
+    return nullptr;
+}
+
+status_t SensorService::SensorDirectConnection::enableDisable(
+        int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
+        int reservedFlags) {
+    // SensorDirectConnection does not support enableDisable, parameters not used
+    UNUSED(handle);
+    UNUSED(enabled);
+    UNUSED(samplingPeriodNs);
+    UNUSED(maxBatchReportLatencyNs);
+    UNUSED(reservedFlags);
+    return INVALID_OPERATION;
+}
+
+status_t SensorService::SensorDirectConnection::setEventRate(
+        int handle, nsecs_t samplingPeriodNs) {
+    // SensorDirectConnection does not support setEventRate, parameters not used
+    UNUSED(handle);
+    UNUSED(samplingPeriodNs);
+    return INVALID_OPERATION;
+}
+
+status_t SensorService::SensorDirectConnection::flush() {
+    // SensorDirectConnection does not support flush
+    return INVALID_OPERATION;
+}
+
+int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int rateLevel) {
+
+    if (handle == -1 && rateLevel == SENSOR_DIRECT_RATE_STOP) {
+        stopAll();
+        return NO_ERROR;
+    }
+
+    if (mService->isOperationRestricted(mOpPackageName)) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+    if (si == nullptr) {
+        return NAME_NOT_FOUND;
+    }
+
+    const Sensor& s = si->getSensor();
+    if (!SensorService::canAccessSensor(s, "config direct channel", mOpPackageName)) {
+        return PERMISSION_DENIED;
+    }
+
+    if (s.getHighestDirectReportRateLevel() == 0
+            || rateLevel > s.getHighestDirectReportRateLevel()
+            || !s.isDirectChannelTypeSupported(mMem.type)) {
+        return INVALID_OPERATION;
+    }
+
+    struct sensors_direct_cfg_t config = {
+        .rate_level = rateLevel
+    };
+
+    Mutex::Autolock _l(mConnectionLock);
+    SensorDevice& dev(SensorDevice::getInstance());
+    int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+
+    if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
+        if (ret == NO_ERROR) {
+            mActivated.erase(handle);
+        } else if (ret > 0) {
+            ret = UNKNOWN_ERROR;
+        }
+    } else {
+        if (ret > 0) {
+            mActivated[handle] = rateLevel;
+        }
+    }
+
+    return ret;
+}
+
+void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
+
+    struct sensors_direct_cfg_t config = {
+        .rate_level = SENSOR_DIRECT_RATE_STOP
+    };
+
+    Mutex::Autolock _l(mConnectionLock);
+    SensorDevice& dev(SensorDevice::getInstance());
+    for (auto &i : mActivated) {
+        dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+    }
+
+    if (backupRecord && mActivatedBackup.empty()) {
+        mActivatedBackup = mActivated;
+    }
+    mActivated.clear();
+}
+
+void SensorService::SensorDirectConnection::recoverAll() {
+    stopAll(false);
+
+    Mutex::Autolock _l(mConnectionLock);
+    SensorDevice& dev(SensorDevice::getInstance());
+
+    // recover list of report from backup
+    mActivated = mActivatedBackup;
+    mActivatedBackup.clear();
+
+    // re-enable them
+    for (auto &i : mActivated) {
+        struct sensors_direct_cfg_t config = {
+            .rate_level = i.second
+        };
+        dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+    }
+}
+
+int32_t SensorService::SensorDirectConnection::getHalChannelHandle() const {
+    return mHalChannelHandle;
+}
+
+bool SensorService::SensorDirectConnection::isEquivalent(const sensors_direct_mem_t *mem) const {
+    bool ret = false;
+
+    if (mMem.type == mem->type) {
+        switch (mMem.type) {
+            case SENSOR_DIRECT_MEM_TYPE_ASHMEM: {
+                // there is no known method to test if two ashmem fds are equivalent besides
+                // trivially comparing the fd values (ino number from fstat() are always the
+                // same, pointing to "/dev/ashmem").
+                int fd1 = mMem.handle->data[0];
+                int fd2 = mem->handle->data[0];
+                ret = (fd1 == fd2);
+                break;
+            }
+            case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+                // there is no known method to test if two gralloc handle are equivalent
+                ret = false;
+                break;
+            default:
+                // should never happen
+                ALOGE("Unexpected mem type %d", mMem.type);
+                ret = true;
+                break;
+        }
+    }
+    return ret;
+}
+
+} // namespace android
+
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
new file mode 100644
index 0000000..27458d4
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
+#define ANDROID_SENSOR_DIRECT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/BinderService.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+class SensorService;
+class BitTube;
+
+class SensorService::SensorDirectConnection: public BnSensorEventConnection {
+public:
+    SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
+            const sensors_direct_mem_t *mem, int32_t halChannelHandle,
+            const String16& opPackageName);
+    void dump(String8& result) const;
+    uid_t getUid() const { return mUid; }
+    int32_t getHalChannelHandle() const;
+    bool isEquivalent(const sensors_direct_mem_t *mem) const;
+
+    // stop all active sensor report. if backupRecord is set to false,
+    // those report can be recovered by recoverAll
+    // called by SensorService when enter restricted mode
+    void stopAll(bool clearRecord = false);
+
+    // recover sensor reports previously stopped by stopAll(true)
+    // called by SensorService when return to NORMAL mode.
+    void recoverAll();
+
+protected:
+    virtual ~SensorDirectConnection();
+    // ISensorEventConnection functions
+    virtual void onFirstRef();
+    virtual sp<BitTube> getSensorChannel() const;
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags);
+    virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
+    virtual status_t flush();
+    virtual int32_t configureChannel(int handle, int rateLevel);
+
+private:
+    const sp<SensorService> mService;
+    const uid_t mUid;
+    const sensors_direct_mem_t mMem;
+    const int32_t mHalChannelHandle;
+    const String16 mOpPackageName;
+
+    mutable Mutex mConnectionLock;
+    std::unordered_map<int, int> mActivated;
+    std::unordered_map<int, int> mActivatedBackup;
+};
+
+} // namepsace android
+
+#endif // ANDROID_SENSOR_DIRECT_CONNECTION_H
+
diff --git a/services/sensorservice/SensorEventAckReceiver.h b/services/sensorservice/SensorEventAckReceiver.h
index 998597a..20fa4c7 100644
--- a/services/sensorservice/SensorEventAckReceiver.h
+++ b/services/sensorservice/SensorEventAckReceiver.h
@@ -27,7 +27,7 @@
     sp<SensorService> const mService;
 public:
     virtual bool threadLoop();
-    SensorEventAckReceiver(const sp<SensorService>& service)
+    explicit SensorEventAckReceiver(const sp<SensorService>& service)
             : mService(service) {
     }
 };
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f2f1444..fad046c 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -17,12 +17,14 @@
 #include <sys/socket.h>
 #include <utils/threads.h>
 
-#include <gui/SensorEventQueue.h>
+#include <sensor/SensorEventQueue.h>
 
 #include "vec.h"
 #include "SensorEventConnection.h"
 #include "SensorDevice.h"
 
+#define UNUSED(x) (void)(x)
+
 namespace android {
 
 SensorService::SensorEventConnection::SensorEventConnection(
@@ -524,6 +526,13 @@
     return  mService->flushSensor(this, mOpPackageName);
 }
 
+int32_t SensorService::SensorEventConnection::configureChannel(int handle, int rateLevel) {
+    // SensorEventConnection does not support configureChannel, parameters not used
+    UNUSED(handle);
+    UNUSED(rateLevel);
+    return INVALID_OPERATION;
+}
+
 int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* /*data*/) {
     if (events & ALOOPER_EVENT_HANGUP || events & ALOOPER_EVENT_ERROR) {
         {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 883c16e..c81e015 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -31,10 +31,10 @@
 
 #include <binder/BinderService.h>
 
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
 
 #include "SensorService.h"
 
@@ -74,6 +74,8 @@
                                    nsecs_t maxBatchReportLatencyNs, int reservedFlags);
     virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
     virtual status_t flush();
+    virtual int32_t configureChannel(int handle, int rateLevel);
+
     // Count the number of flush complete events which are about to be dropped in the buffer.
     // Increment mPendingFlushEventsToSend in mSensorInfo. These flush complete events will be sent
     // separately before the next batch of events.
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 9863f62..414f673 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -163,7 +163,7 @@
     }
     mSensorDevice.batch(ident, mAcc.getHandle(), 0, ns, 0);
     if (mode != FUSION_NOMAG) {
-        mSensorDevice.batch(ident, mMag.getHandle(), 0, ms2ns(20), 0);
+        mSensorDevice.batch(ident, mMag.getHandle(), 0, ms2ns(10), 0);
     }
     if (mode != FUSION_NOGYRO) {
         mSensorDevice.batch(ident, mGyro.getHandle(), 0, mTargetDelayNs, 0);
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index ad636d5..8c0fbf9 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -24,7 +24,7 @@
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "Fusion.h"
 
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index dafcf2d..b5375cb 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_SENSOR_INTERFACE_H
 #define ANDROID_SENSOR_INTERFACE_H
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 #include <utils/RefBase.h>
 
 // ---------------------------------------------------------------------------
@@ -47,7 +47,7 @@
 
 class BaseSensor : public SensorInterface {
 public:
-    BaseSensor(const sensor_t& sensor);
+    explicit BaseSensor(const sensor_t& sensor);
     BaseSensor(const sensor_t& sensor, const uint8_t (&uuid)[16]);
 
     // Not all sensors need to support batching.
@@ -74,7 +74,7 @@
 
 class HardwareSensor : public BaseSensor {
 public:
-    HardwareSensor(const sensor_t& sensor);
+    explicit HardwareSensor(const sensor_t& sensor);
     HardwareSensor(const sensor_t& sensor, const uint8_t (&uuid)[16]);
 
     virtual ~HardwareSensor();
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index e0101c1..ab08cac 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -124,7 +124,7 @@
     forEachSensor([&result] (const Sensor& s) -> bool {
             result.appendFormat(
                     "%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
-                        ") | perm: %s\n\t",
+                        ") | perm: %s\n",
                     s.getHandle(),
                     s.getName().string(),
                     s.getVendor().string(),
@@ -133,17 +133,18 @@
                     s.getType(),
                     s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a");
 
+            result.append("\t");
             const int reportingMode = s.getReportingMode();
             if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
-                result.append(" continuous | ");
+                result.append("continuous | ");
             } else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
-                result.append(" on-change | ");
+                result.append("on-change | ");
             } else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
-                result.append(" one-shot | ");
+                result.append("one-shot | ");
             } else if (reportingMode == AREPORTING_MODE_SPECIAL_TRIGGER) {
-                result.append(" special-trigger | ");
+                result.append("special-trigger | ");
             } else {
-                result.append(" unknown-mode | ");
+                result.append("unknown-mode | ");
             }
 
             if (s.getMaxDelay() > 0) {
@@ -178,8 +179,20 @@
             if (s.hasAdditionalInfo()) {
                 result.appendFormat("has-additional-info, ");
             }
-
             result.append("\n");
+
+            if (s.getHighestDirectReportRateLevel() > SENSOR_DIRECT_RATE_STOP) {
+                result.appendFormat("\thighest rate level = %d, support shared mem: ",
+                        s.getHighestDirectReportRateLevel());
+                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM)) {
+                    result.append("ashmem, ");
+                }
+                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC)) {
+                    result.append("gralloc, ");
+                }
+                result.appendFormat("flag =0x%08x", static_cast<int>(s.getFlags()));
+                result.append("\n");
+            }
             return true;
         });
     return std::string(result.string());
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8209d96..6b90ad9 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -20,7 +20,7 @@
 #include "SensorInterface.h"
 #include "SensorServiceUtils.h"
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index 54d815b..75e8989 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -17,29 +17,76 @@
 #ifndef ANDROID_SENSOR_REGISTRATION_INFO_H
 #define ANDROID_SENSOR_REGISTRATION_INFO_H
 
+#include "SensorServiceUtils.h"
+#include <utils/Thread.h>
+#include <iomanip>
+#include <sstream>
+
 namespace android {
 
 class SensorService;
 
-struct SensorService::SensorRegistrationInfo {
-    int32_t mSensorHandle;
-    String8 mPackageName;
-    bool mActivated;
-    int32_t mSamplingRateUs;
-    int32_t mMaxReportLatencyUs;
-    int32_t mHour, mMin, mSec;
-
+class SensorService::SensorRegistrationInfo : public SensorServiceUtil::Dumpable {
+public:
     SensorRegistrationInfo() : mPackageName() {
         mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
-        mHour = mMin = mSec = INT32_MIN;
+        mHour = mMin = mSec = INT8_MIN;
         mActivated = false;
     }
 
-    static bool isSentinel(const SensorRegistrationInfo& info) {
-       return (info.mHour == INT32_MIN &&
-               info.mMin == INT32_MIN &&
-               info.mSec == INT32_MIN);
+    SensorRegistrationInfo(int32_t handle, const String8 &packageName,
+                           int32_t samplingRateNs, int32_t maxReportLatencyNs, bool activate) {
+        mSensorHandle = handle;
+        mPackageName = packageName;
+
+        mSamplingRateUs = static_cast<int32_t>(samplingRateNs/1000);
+        mMaxReportLatencyUs = static_cast<int32_t>(maxReportLatencyNs/1000);
+        mActivated = activate;
+
+        IPCThreadState *thread = IPCThreadState::self();
+        mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
+        mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
+
+        time_t rawtime = time(NULL);
+        struct tm * timeinfo = localtime(&rawtime);
+        mHour = static_cast<int8_t>(timeinfo->tm_hour);
+        mMin = static_cast<int8_t>(timeinfo->tm_min);
+        mSec = static_cast<int8_t>(timeinfo->tm_sec);
     }
+
+    static bool isSentinel(const SensorRegistrationInfo& info) {
+       return (info.mHour == INT8_MIN &&
+               info.mMin == INT8_MIN &&
+               info.mSec == INT8_MIN);
+    }
+
+    // Dumpable interface
+    virtual std::string dump() const override {
+        std::ostringstream ss;
+        ss << std::setfill('0') << std::setw(2) << static_cast<int>(mHour) << ":"
+           << std::setw(2) << static_cast<int>(mMin) << ":"
+           << std::setw(2) << static_cast<int>(mSec)
+           << (mActivated ? " +" : " -")
+           << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
+           << std::setfill(' ') << " pid=" << std::setw(5) << mPid
+           << " uid=" << std::setw(5) << mUid << " package=" << mPackageName;
+        if (mActivated) {
+           ss  << " samplingPeriod=" << mSamplingRateUs << "us"
+               << " batchingPeriod=" << mMaxReportLatencyUs << "us";
+        };
+        return ss.str();
+    }
+
+private:
+    int32_t mSensorHandle;
+    String8 mPackageName;
+    pid_t   mPid;
+    uid_t   mUid;
+    int32_t mSamplingRateUs;
+    int32_t mMaxReportLatencyUs;
+    bool mActivated;
+    int8_t mHour, mMin, mSec;
+
 };
 
 } // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 99da2c4..d60768c 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,22 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#include <cutils/properties.h>
-
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
-
-#include <gui/SensorEventQueue.h>
-
+#include <cutils/ashmem.h>
+#include <cutils/properties.h>
 #include <hardware/sensors.h>
 #include <hardware_legacy/power.h>
-
 #include <openssl/digest.h>
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
+#include <sensor/SensorEventQueue.h>
+#include <utils/SystemClock.h>
 
 #include "BatteryService.h"
 #include "CorrectedGyroSensor.h"
@@ -40,6 +37,7 @@
 #include "SensorInterface.h"
 
 #include "SensorService.h"
+#include "SensorDirectConnection.h"
 #include "SensorEventAckReceiver.h"
 #include "SensorEventConnection.h"
 #include "SensorRecord.h"
@@ -75,7 +73,8 @@
 #define SENSOR_SERVICE_SCHED_FIFO_PRIORITY 10
 
 // Permissions.
-static const String16 sDump("android.permission.DUMP");
+static const String16 sDumpPermission("android.permission.DUMP");
+static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
 
 SensorService::SensorService()
     : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
@@ -317,7 +316,7 @@
 
 status_t SensorService::dump(int fd, const Vector<String16>& args) {
     String8 result;
-    if (!PermissionCache::checkCallingPermission(sDump)) {
+    if (!PermissionCache::checkCallingPermission(sDumpPermission)) {
         result.appendFormat("Permission Denial: can't dump SensorService from pid=%d, uid=%d\n",
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
@@ -337,7 +336,16 @@
             if (mCurrentOperatingMode != NORMAL) {
                 return INVALID_OPERATION;
             }
+
             mCurrentOperatingMode = RESTRICTED;
+            // temporarily stop all sensor direct report
+            for (auto &i : mDirectConnections) {
+                sp<SensorDirectConnection> connection(i.promote());
+                if (connection != nullptr) {
+                    connection->stopAll(true /* backupRecord */);
+                }
+            }
+
             dev.disableAllSensors();
             // Clear all pending flush connections for all active sensors. If one of the active
             // connections has called flush() and the underlying sensor has been disabled before a
@@ -352,6 +360,13 @@
             if (mCurrentOperatingMode == RESTRICTED) {
                 mCurrentOperatingMode = NORMAL;
                 dev.enableAllSensors();
+                // recover all sensor direct report
+                for (auto &i : mDirectConnections) {
+                    sp<SensorDirectConnection> connection(i.promote());
+                    if (connection != nullptr) {
+                        connection->recoverAll();
+                    }
+                }
             }
             if (mCurrentOperatingMode == DATA_INJECTION) {
                resetToNormalModeLocked();
@@ -430,8 +445,8 @@
                case DATA_INJECTION:
                    result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string());
             }
-            result.appendFormat("%zd active connections\n", mActiveConnections.size());
 
+            result.appendFormat("%zd active connections\n", mActiveConnections.size());
             for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
                 sp<SensorEventConnection> connection(mActiveConnections[i].promote());
                 if (connection != 0) {
@@ -440,6 +455,15 @@
                 }
             }
 
+            result.appendFormat("%zd direct connections\n", mDirectConnections.size());
+            for (size_t i = 0 ; i < mDirectConnections.size() ; i++) {
+                sp<SensorDirectConnection> connection(mDirectConnections[i].promote());
+                if (connection != nullptr) {
+                    result.appendFormat("Direct connection %zu:\n", i);
+                    connection->dump(result);
+                }
+            }
+
             result.appendFormat("Previous Registrations:\n");
             // Log in the reverse chronological order.
             int currentIndex = (mNextSensorRegIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
@@ -453,17 +477,7 @@
                         SENSOR_REGISTRATIONS_BUF_SIZE;
                     continue;
                 }
-                if (reg_info.mActivated) {
-                   result.appendFormat("%02d:%02d:%02d activated handle=0x%08x "
-                           "samplingRate=%dus maxReportLatency=%dus package=%s\n",
-                           reg_info.mHour, reg_info.mMin, reg_info.mSec, reg_info.mSensorHandle,
-                           reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs,
-                           reg_info.mPackageName.string());
-                } else {
-                   result.appendFormat("%02d:%02d:%02d de-activated handle=0x%08x package=%s\n",
-                           reg_info.mHour, reg_info.mMin, reg_info.mSec,
-                           reg_info.mSensorHandle, reg_info.mPackageName.string());
-                }
+                result.appendFormat("%s\n", reg_info.dump().c_str());
                 currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
                         SENSOR_REGISTRATIONS_BUF_SIZE;
             } while(startIndex != currentIndex);
@@ -868,7 +882,7 @@
     }
 }
 
-Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
+Vector<Sensor> SensorService::getSensorList(const String16& /* opPackageName */) {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sensors", value, "0");
     const Vector<Sensor>& initialSensorList = (atoi(value)) ?
@@ -876,14 +890,7 @@
     Vector<Sensor> accessibleSensorList;
     for (size_t i = 0; i < initialSensorList.size(); i++) {
         Sensor sensor = initialSensorList[i];
-        if (canAccessSensor(sensor, "getSensorList", opPackageName)) {
-            accessibleSensorList.add(sensor);
-        } else {
-            ALOGI("Skipped sensor %s because it requires permission %s and app op %d",
-                  sensor.getName().string(),
-                  sensor.getRequiredPermission().string(),
-                  sensor.getRequiredAppOp());
-        }
+        accessibleSensorList.add(sensor);
     }
     makeUuidsIntoIdsForSensorList(accessibleSensorList);
     return accessibleSensorList;
@@ -943,6 +950,165 @@
     return (mCurrentOperatingMode == DATA_INJECTION);
 }
 
+sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
+        const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
+        const native_handle *resource) {
+    Mutex::Autolock _l(mLock);
+
+    struct sensors_direct_mem_t mem = {
+        .type = type,
+        .format = format,
+        .size = size,
+        .handle = resource,
+    };
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+
+    if (mem.handle == nullptr) {
+        ALOGE("Failed to clone resource handle");
+        return nullptr;
+    }
+
+    // check format
+    if (format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+        ALOGE("Direct channel format %d is unsupported!", format);
+        return nullptr;
+    }
+
+    // check for duplication
+    for (auto &i : mDirectConnections) {
+        sp<SensorDirectConnection> connection(i.promote());
+        if (connection != nullptr && connection->isEquivalent(&mem)) {
+            ALOGE("Duplicate create channel request for the same share memory");
+            return nullptr;
+        }
+    }
+
+    // check specific to memory type
+    switch(type) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM: { // channel backed by ashmem
+            int fd = resource->data[0];
+            int size2 = ashmem_get_size_region(fd);
+            // check size consistency
+            if (size2 < static_cast<int>(size)) {
+                ALOGE("Ashmem direct channel size %" PRIu32 " greater than shared memory size %d",
+                      size, size2);
+                return nullptr;
+            }
+            break;
+        }
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            // no specific checks for gralloc
+            break;
+        default:
+            ALOGE("Unknown direct connection memory type %d", type);
+            return nullptr;
+    }
+
+    native_handle_t *clone = native_handle_clone(resource);
+    if (!clone) {
+        return nullptr;
+    }
+
+    SensorDirectConnection* conn = nullptr;
+    SensorDevice& dev(SensorDevice::getInstance());
+    int channelHandle = dev.registerDirectChannel(&mem);
+
+    if (channelHandle <= 0) {
+        ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
+    } else {
+        mem.handle = clone;
+        conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName);
+    }
+
+    if (conn == nullptr) {
+        native_handle_close(clone);
+        native_handle_delete(clone);
+    } else {
+        // add to list of direct connections
+        // sensor service should never hold pointer or sp of SensorDirectConnection object.
+        mDirectConnections.add(wp<SensorDirectConnection>(conn));
+    }
+    return conn;
+}
+
+int SensorService::setOperationParameter(
+            int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) {
+    Mutex::Autolock _l(mLock);
+
+    // check permission
+    int32_t uid;
+    bool hasPermission = checkCallingPermission(sLocationHardwarePermission, nullptr, &uid);
+    if (!hasPermission || (uid != 1000 && uid != 0)) {
+        return PERMISSION_DENIED;
+    }
+
+    bool isFloat = true;
+    size_t expectSize = INT32_MAX;
+    switch (type) {
+        case AINFO_LOCAL_GEOMAGNETIC_FIELD:
+            isFloat = true;
+            expectSize = 3;
+            break;
+        case AINFO_LOCAL_GRAVITY:
+            isFloat = true;
+            expectSize = 1;
+            break;
+        case AINFO_DOCK_STATE:
+        case AINFO_HIGH_PERFORMANCE_MODE:
+        case AINFO_MAGNETIC_FIELD_CALIBRATION:
+            isFloat = false;
+            expectSize = 1;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    // three events: first one is begin tag, last one is end tag, the one in the middle
+    // is the payload.
+    sensors_event_t event[3];
+    int64_t timestamp = elapsedRealtimeNano();
+    for (sensors_event_t* i = event; i < event + 3; i++) {
+        *i = (sensors_event_t) {
+            .version = sizeof(sensors_event_t),
+            .sensor = SENSORS_HANDLE_BASE - 1, // sensor that never exists
+            .type = SENSOR_TYPE_ADDITIONAL_INFO,
+            .timestamp = timestamp++,
+            .additional_info = (additional_info_event_t) {
+                .serial = 0
+            }
+        };
+    }
+
+    event[0].additional_info.type = AINFO_BEGIN;
+    event[1].additional_info.type = type;
+    event[2].additional_info.type = AINFO_END;
+
+    if (isFloat) {
+        if (floats.size() != expectSize) {
+            return BAD_VALUE;
+        }
+        for (size_t i = 0; i < expectSize; ++i) {
+            event[1].additional_info.data_float[i] = floats[i];
+        }
+    } else {
+        if (ints.size() != expectSize) {
+            return BAD_VALUE;
+        }
+        for (size_t i = 0; i < expectSize; ++i) {
+            event[1].additional_info.data_int32[i] = ints[i];
+        }
+    }
+
+    SensorDevice& dev(SensorDevice::getInstance());
+    for (sensors_event_t* i = event; i < event + 3; i++) {
+        int ret = dev.injectSensorData(i);
+        if (ret != NO_ERROR) {
+            return ret;
+        }
+    }
+    return NO_ERROR;
+}
+
 status_t SensorService::resetToNormalMode() {
     Mutex::Autolock _l(mLock);
     return resetToNormalModeLocked();
@@ -950,9 +1116,11 @@
 
 status_t SensorService::resetToNormalModeLocked() {
     SensorDevice& dev(SensorDevice::getInstance());
-    dev.enableAllSensors();
     status_t err = dev.setMode(NORMAL);
-    mCurrentOperatingMode = NORMAL;
+    if (err == NO_ERROR) {
+        mCurrentOperatingMode = NORMAL;
+        dev.enableAllSensors();
+    }
     return err;
 }
 
@@ -1000,11 +1168,18 @@
     dev.notifyConnectionDestroyed(c);
 }
 
+void SensorService::cleanupConnection(SensorDirectConnection* c) {
+    Mutex::Autolock _l(mLock);
+
+    SensorDevice& dev(SensorDevice::getInstance());
+    dev.unregisterDirectChannel(c->getHalChannelHandle());
+    mDirectConnections.remove(c);
+}
+
 sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
     return mSensors.getInterface(handle);
 }
 
-
 status_t SensorService::enable(const sp<SensorEventConnection>& connection,
         int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
         const String16& opPackageName) {
@@ -1018,7 +1193,7 @@
     }
 
     Mutex::Autolock _l(mLock);
-    if ((mCurrentOperatingMode == RESTRICTED || mCurrentOperatingMode == DATA_INJECTION)
+    if (mCurrentOperatingMode != NORMAL
            && !isWhiteListedPackage(connection->getPackageName())) {
         return INVALID_OPERATION;
     }
@@ -1074,6 +1249,12 @@
             handle, connection.get());
     }
 
+    // Check maximum delay for the sensor.
+    nsecs_t maxDelayNs = sensor->getSensor().getMaxDelay() * 1000LL;
+    if (maxDelayNs > 0 && (samplingPeriodNs > maxDelayNs)) {
+        samplingPeriodNs = maxDelayNs;
+    }
+
     nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs();
     if (samplingPeriodNs < minDelayNs) {
         samplingPeriodNs = minDelayNs;
@@ -1112,18 +1293,10 @@
 
     if (err == NO_ERROR) {
         connection->updateLooperRegistration(mLooper);
-        SensorRegistrationInfo &reg_info =
-            mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
-        reg_info.mSensorHandle = handle;
-        reg_info.mSamplingRateUs = samplingPeriodNs/1000;
-        reg_info.mMaxReportLatencyUs = maxBatchReportLatencyNs/1000;
-        reg_info.mActivated = true;
-        reg_info.mPackageName = connection->getPackageName();
-        time_t rawtime = time(NULL);
-        struct tm * timeinfo = localtime(&rawtime);
-        reg_info.mHour = timeinfo->tm_hour;
-        reg_info.mMin = timeinfo->tm_min;
-        reg_info.mSec = timeinfo->tm_sec;
+
+        mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+                SensorRegistrationInfo(handle, connection->getPackageName(),
+                                       samplingPeriodNs, maxBatchReportLatencyNs, true);
         mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
     }
 
@@ -1146,16 +1319,8 @@
 
     }
     if (err == NO_ERROR) {
-        SensorRegistrationInfo &reg_info =
-            mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
-        reg_info.mActivated = false;
-        reg_info.mPackageName= connection->getPackageName();
-        reg_info.mSensorHandle = handle;
-        time_t rawtime = time(NULL);
-        struct tm * timeinfo = localtime(&rawtime);
-        reg_info.mHour = timeinfo->tm_hour;
-        reg_info.mMin = timeinfo->tm_min;
-        reg_info.mSec = timeinfo->tm_sec;
+        mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+                SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false);
         mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
     }
     return err;
@@ -1336,5 +1501,13 @@
     return (packageName.contains(mWhiteListedPackage.string()));
 }
 
-}; // namespace android
+bool SensorService::isOperationRestricted(const String16& opPackageName) {
+    Mutex::Autolock _l(mLock);
+    if (mCurrentOperatingMode != RESTRICTED) {
+        String8 package(opPackageName);
+        return !isWhiteListedPackage(package);
+    }
+    return false;
+}
 
+}; // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 4a63ef0..2a9d6e8 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -22,9 +22,9 @@
 
 #include <binder/BinderService.h>
 #include <cutils/compiler.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
 
 #include <utils/AndroidThreads.h>
 #include <utils/KeyedVector.h>
@@ -49,9 +49,9 @@
 #define IGNORE_HARDWARE_FUSION  false
 #define DEBUG_CONNECTIONS   false
 // Max size is 100 KB which is enough to accept a batch of about 1000 events.
-#define MAX_SOCKET_BUFFER_SIZE_BATCHED 100 * 1024
+#define MAX_SOCKET_BUFFER_SIZE_BATCHED (100 * 1024)
 // For older HALs which don't support batching, use a smaller socket buffer size.
-#define SOCKET_BUFFER_SIZE_NON_BATCHED 4 * 1024
+#define SOCKET_BUFFER_SIZE_NON_BATCHED (4 * 1024)
 
 #define SENSOR_REGISTRATIONS_BUF_SIZE 200
 
@@ -67,9 +67,11 @@
 {
     // nested class/struct for internal use
     class SensorEventConnection;
+    class SensorDirectConnection;
 
 public:
     void cleanupConnection(SensorEventConnection* connection);
+    void cleanupConnection(SensorDirectConnection* c);
 
     status_t enable(const sp<SensorEventConnection>& connection, int handle,
                     nsecs_t samplingPeriodNs,  nsecs_t maxBatchReportLatencyNs, int reservedFlags,
@@ -89,7 +91,7 @@
     // nested class/struct for internal use
     class SensorRecord;
     class SensorEventAckReceiver;
-    struct SensorRegistrationInfo;
+    class SensorRegistrationInfo;
 
     enum Mode {
        // The regular operating mode where any application can register/unregister/call flush on
@@ -154,6 +156,10 @@
             const String8& packageName,
             int requestedMode, const String16& opPackageName);
     virtual int isDataInjectionEnabled();
+    virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+            uint32_t size, int32_t type, int32_t format, const native_handle *resource);
+    virtual int setOperationParameter(
+            int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
     virtual status_t dump(int fd, const Vector<String16>& args);
 
     String8 getSensorName(int handle) const;
@@ -203,6 +209,7 @@
     // allowed to register for or call flush on sensors. Typically only cts test packages are
     // allowed.
     bool isWhiteListedPackage(const String8& packageName);
+    bool isOperationRestricted(const String16& opPackageName);
 
     // Reset the state of SensorService to NORMAL mode.
     status_t resetToNormalMode();
@@ -239,6 +246,7 @@
     sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
     wp<const SensorEventConnection> * mMapFlushEventsToConnections;
     std::unordered_map<int, RecentEventLogger*> mRecentEvent;
+    SortedVector< wp<SensorDirectConnection> > mDirectConnections;
     Mode mCurrentOperatingMode;
 
     // This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 1996a00..34cd8dd 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -54,6 +54,7 @@
         case SENSOR_TYPE_STATIONARY_DETECT:
         case SENSOR_TYPE_MOTION_DETECT:
         case SENSOR_TYPE_HEART_BEAT:
+        case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
             return 1;
 
         default:
diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp
new file mode 100644
index 0000000..748dafc
--- /dev/null
+++ b/services/sensorservice/hidl/Android.bp
@@ -0,0 +1,32 @@
+cc_library_shared {
+    name: "libsensorservicehidl",
+    srcs: [
+        "EventQueue.cpp",
+        "DirectReportChannel.cpp",
+        "SensorManager.cpp",
+        "utils.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+        "libsensor",
+        "android.frameworks.sensorservice@1.0",
+        "android.hardware.sensors@1.0",
+        "android.hidl.base@1.0",
+    ],
+    static_libs: [
+        "android.hardware.sensors@1.0-convert",
+    ],
+    export_include_dirs: [
+        "include/"
+    ],
+    local_include_dirs: [
+        "include/sensorservicehidl/"
+    ]
+}
diff --git a/services/sensorservice/hidl/DirectReportChannel.cpp b/services/sensorservice/hidl/DirectReportChannel.cpp
new file mode 100644
index 0000000..adc4675
--- /dev/null
+++ b/services/sensorservice/hidl/DirectReportChannel.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DirectReportChannel.h"
+#include "utils.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId)
+        : mManager(manager), mId(channelId) {}
+
+DirectReportChannel::~DirectReportChannel() {
+    mManager.destroyDirectChannel(mId);
+}
+
+// Methods from ::android::frameworks::sensorservice::V1_0::IDirectReportChannel follow.
+Return<void> DirectReportChannel::configure(int32_t sensorHandle, RateLevel rate,
+        configure_cb _hidl_cb) {
+    int token = mManager.configureDirectChannel(mId,
+            static_cast<int>(sensorHandle), static_cast<int>(rate));
+    _hidl_cb(token <= 0 ? 0 : token,
+             token <= 0 ? convertResult(token) : Result::OK);
+    return Void();
+}
+
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/sensorservice/hidl/DirectReportChannel.h b/services/sensorservice/hidl/DirectReportChannel.h
new file mode 100644
index 0000000..dd67827
--- /dev/null
+++ b/services/sensorservice/hidl/DirectReportChannel.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_DIRECTREPORTCHANNEL_H
+#define ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_DIRECTREPORTCHANNEL_H
+
+#include <android/frameworks/sensorservice/1.0/IDirectReportChannel.h>
+#include <android/frameworks/sensorservice/1.0/types.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::frameworks::sensorservice::V1_0::IDirectReportChannel;
+using ::android::hardware::sensors::V1_0::RateLevel;
+using ::android::hidl::base::V1_0::DebugInfo;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct DirectReportChannel final : public IDirectReportChannel {
+
+    DirectReportChannel(::android::SensorManager& manager, int channelId);
+    ~DirectReportChannel();
+
+    // Methods from ::android::frameworks::sensorservice::V1_0::IDirectReportChannel follow.
+    Return<void> configure(int32_t sensorHandle, RateLevel rate,
+            configure_cb _hidl_cb) override;
+
+private:
+    ::android::SensorManager& mManager;
+    const int mId;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_DIRECTREPORTCHANNEL_H
diff --git a/services/sensorservice/hidl/EventQueue.cpp b/services/sensorservice/hidl/EventQueue.cpp
new file mode 100644
index 0000000..ff20066
--- /dev/null
+++ b/services/sensorservice/hidl/EventQueue.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EventQueue.h"
+#include "utils.h"
+
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+class EventQueueLooperCallback : public ::android::LooperCallback {
+public:
+    EventQueueLooperCallback(sp<::android::SensorEventQueue> queue,
+                             sp<IEventQueueCallback> callback)
+            : mQueue(queue), mCallback(callback) {
+    }
+
+    int handleEvent(__unused int fd, __unused int events, __unused void* data) {
+
+        ASensorEvent event;
+        ssize_t actual;
+
+        auto internalQueue = mQueue.promote();
+        if (internalQueue == nullptr) {
+            return 1;
+        }
+
+        while ((actual = internalQueue->read(&event, 1 /* count */)) > 0) {
+            internalQueue->sendAck(&event, actual);
+            Return<void> ret = mCallback->onEvent(convertEvent(event));
+            (void)ret.isOk(); // ignored
+        }
+
+        return 1; // continue to receive callbacks
+    }
+
+private:
+    wp<::android::SensorEventQueue> mQueue;
+    sp<IEventQueueCallback> mCallback;
+};
+
+EventQueue::EventQueue(
+        sp<IEventQueueCallback> callback,
+        sp<::android::Looper> looper,
+        sp<::android::SensorEventQueue> internalQueue)
+            : mLooper(looper),
+              mInternalQueue(internalQueue) {
+
+    mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
+            new EventQueueLooperCallback(internalQueue, callback), NULL /* data */);
+}
+
+void EventQueue::onLastStrongRef(const void *id) {
+    IEventQueue::onLastStrongRef(id);
+    mLooper->removeFd(mInternalQueue->getFd());
+}
+
+// Methods from ::android::frameworks::sensorservice::V1_0::IEventQueue follow.
+Return<Result> EventQueue::enableSensor(int32_t sensorHandle, int32_t samplingPeriodUs,
+        int64_t maxBatchReportLatencyUs) {
+    return convertResult(mInternalQueue->enableSensor(sensorHandle, samplingPeriodUs,
+            maxBatchReportLatencyUs, 0 /* reserved flags */));
+}
+
+Return<Result> EventQueue::disableSensor(int32_t sensorHandle) {
+    return convertResult(mInternalQueue->disableSensor(sensorHandle));
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/sensorservice/hidl/EventQueue.h b/services/sensorservice/hidl/EventQueue.h
new file mode 100644
index 0000000..6be03b7
--- /dev/null
+++ b/services/sensorservice/hidl/EventQueue.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_EVENTQUEUE_H
+#define ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_EVENTQUEUE_H
+
+#include "SensorManager.h"
+
+#include <android/frameworks/sensorservice/1.0/IEventQueue.h>
+#include <android/frameworks/sensorservice/1.0/IEventQueueCallback.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::frameworks::sensorservice::V1_0::IEventQueue;
+using ::android::frameworks::sensorservice::V1_0::IEventQueueCallback;
+using ::android::frameworks::sensorservice::V1_0::Result;
+using ::android::hardware::Return;
+using ::android::sp;
+
+struct EventQueue final : public IEventQueue {
+    EventQueue(
+        sp<IEventQueueCallback> callback,
+        sp<::android::Looper> looper,
+        sp<::android::SensorEventQueue> internalQueue);
+    void onLastStrongRef(const void *) override;
+
+    // Methods from ::android::frameworks::sensorservice::V1_0::IEventQueue follow.
+    Return<Result> enableSensor(int32_t sensorHandle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs) override;
+    Return<Result> disableSensor(int32_t sensorHandle) override;
+
+private:
+    friend class EventQueueLooperCallback;
+    sp<::android::Looper> mLooper;
+    sp<::android::SensorEventQueue> mInternalQueue;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_EVENTQUEUE_H
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
new file mode 100644
index 0000000..25a3dc5
--- /dev/null
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+// LOG_TAG defined via build flag.
+#ifndef LOG_TAG
+#define LOG_TAG "HidlSensorManager"
+#endif
+#include <android-base/logging.h>
+
+#include "SensorManager.h"
+
+#include <sched.h>
+
+#include <thread>
+
+#include "EventQueue.h"
+#include "DirectReportChannel.h"
+#include "utils.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::sensors::V1_0::SensorInfo;
+using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
+using ::android::sp;
+
+static const char* POLL_THREAD_NAME = "hidl_ssvc_poll";
+
+SensorManager::SensorManager(JavaVM* vm)
+        : mJavaVm(vm) {
+}
+
+SensorManager::~SensorManager() {
+    // Stops pollAll inside the thread.
+    std::unique_lock<std::mutex> lock(mLooperMutex);
+    if (mLooper != nullptr) {
+        mLooper->wake();
+    }
+}
+
+// Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
+Return<void> SensorManager::getSensorList(getSensorList_cb _hidl_cb) {
+    ::android::Sensor const* const* list;
+    ssize_t count = getInternalManager().getSensorList(&list);
+    if (count < 0 || !list) {
+        LOG(ERROR) << "::android::SensorManager::getSensorList encounters " << count;
+        _hidl_cb({}, Result::UNKNOWN_ERROR);
+        return Void();
+    }
+    hidl_vec<SensorInfo> ret;
+    ret.resize(static_cast<size_t>(count));
+    for (ssize_t i = 0; i < count; ++i) {
+        ret[i] = convertSensor(*list[i]);
+    }
+    _hidl_cb(ret, Result::OK);
+    return Void();
+}
+
+Return<void> SensorManager::getDefaultSensor(SensorType type, getDefaultSensor_cb _hidl_cb) {
+    ::android::Sensor const* sensor = getInternalManager().getDefaultSensor(static_cast<int>(type));
+    if (!sensor) {
+        _hidl_cb({}, Result::NOT_EXIST);
+        return Void();
+    }
+    _hidl_cb(convertSensor(*sensor), Result::OK);
+    return Void();
+}
+
+template<typename Callback>
+void createDirectChannel(::android::SensorManager& manager, size_t size, int type,
+        const native_handle_t* handle, const Callback& _hidl_cb) {
+
+    int channelId = manager.createDirectChannel(
+        size, type, handle);
+    if (channelId < 0) {
+        _hidl_cb(nullptr, convertResult(channelId));
+        return;
+    }
+    if (channelId == 0) {
+        _hidl_cb(nullptr, Result::UNKNOWN_ERROR);
+        return;
+    }
+
+    _hidl_cb(sp<IDirectReportChannel>(new DirectReportChannel(manager, channelId)),
+            Result::OK);
+}
+
+Return<void> SensorManager::createAshmemDirectChannel(
+        const hidl_memory& mem, uint64_t size,
+        createAshmemDirectChannel_cb _hidl_cb) {
+    if (size > mem.size() || size < (uint64_t)SensorsEventFormatOffset::TOTAL_LENGTH) {
+        _hidl_cb(nullptr, Result::BAD_VALUE);
+        return Void();
+    }
+
+    createDirectChannel(getInternalManager(), size, SENSOR_DIRECT_MEM_TYPE_ASHMEM,
+            mem.handle(), _hidl_cb);
+
+    return Void();
+}
+
+Return<void> SensorManager::createGrallocDirectChannel(
+        const hidl_handle& buffer, uint64_t size,
+        createGrallocDirectChannel_cb _hidl_cb) {
+
+    createDirectChannel(getInternalManager(), size, SENSOR_DIRECT_MEM_TYPE_GRALLOC,
+            buffer.getNativeHandle(), _hidl_cb);
+
+    return Void();
+}
+
+/* One global looper for all event queues created from this SensorManager. */
+sp<::android::Looper> SensorManager::getLooper() {
+    std::unique_lock<std::mutex> lock(mLooperMutex);
+    if (mLooper == nullptr) {
+        std::condition_variable looperSet;
+
+        std::thread{[&mutex = mLooperMutex, &looper = mLooper, &looperSet, javaVm = mJavaVm] {
+
+            struct sched_param p = {0};
+            p.sched_priority = 10;
+            if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
+                LOG(WARNING) << "Could not use SCHED_FIFO for looper thread: "
+                        << strerror(errno);
+            }
+
+            std::unique_lock<std::mutex> lock(mutex);
+            if (looper != nullptr) {
+                LOG(INFO) << "Another thread has already set the looper, exiting this one.";
+                return;
+            }
+            looper = Looper::prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS /* opts */);
+            lock.unlock();
+
+            // Attach the thread to JavaVM so that pollAll do not crash if the event
+            // is from Java.
+            JavaVMAttachArgs args{
+                .version = JNI_VERSION_1_2,
+                .name = POLL_THREAD_NAME,
+                .group = NULL
+            };
+            JNIEnv* env;
+            if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
+                LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
+            }
+
+            looperSet.notify_one();
+            int pollResult = looper->pollAll(-1 /* timeout */);
+            if (pollResult != ALOOPER_POLL_WAKE) {
+                LOG(ERROR) << "Looper::pollAll returns unexpected " << pollResult;
+            }
+
+            if (javaVm->DetachCurrentThread() != JNI_OK) {
+                LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
+            }
+
+            LOG(INFO) << "Looper thread is terminated.";
+        }}.detach();
+        looperSet.wait(lock, [this]{ return this->mLooper != nullptr; });
+    }
+    return mLooper;
+}
+
+::android::SensorManager& SensorManager::getInternalManager() {
+    std::lock_guard<std::mutex> lock(mInternalManagerMutex);
+    if (mInternalManager == nullptr) {
+        mInternalManager = &::android::SensorManager::getInstanceForPackage(
+                String16(ISensorManager::descriptor));
+    }
+    return *mInternalManager;
+}
+
+Return<void> SensorManager::createEventQueue(
+        const sp<IEventQueueCallback> &callback, createEventQueue_cb _hidl_cb) {
+    if (callback == nullptr) {
+        _hidl_cb(nullptr, Result::BAD_VALUE);
+        return Void();
+    }
+
+    sp<::android::Looper> looper = getLooper();
+    sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue();
+    if (internalQueue == nullptr) {
+        LOG(WARNING) << "::android::SensorManager::createEventQueue returns nullptr.";
+        _hidl_cb(nullptr, Result::UNKNOWN_ERROR);
+        return Void();
+    }
+
+    sp<IEventQueue> queue = new EventQueue(callback, looper, internalQueue);
+    _hidl_cb(queue, Result::OK);
+
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
new file mode 100644
index 0000000..e66c8e5
--- /dev/null
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_SENSORMANAGER_H
+#define ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_SENSORMANAGER_H
+
+#include <jni.h>
+
+#include <mutex>
+
+#include <android/frameworks/sensorservice/1.0/ISensorManager.h>
+#include <android/frameworks/sensorservice/1.0/types.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <sensor/SensorManager.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::sensors::V1_0::SensorType;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::Return;
+
+struct SensorManager final : public ISensorManager {
+
+    SensorManager(JavaVM* vm);
+    ~SensorManager();
+
+    // Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
+    Return<void> getSensorList(getSensorList_cb _hidl_cb) override;
+    Return<void> getDefaultSensor(SensorType type, getDefaultSensor_cb _hidl_cb) override;
+    Return<void> createAshmemDirectChannel(const hidl_memory& mem, uint64_t size, createAshmemDirectChannel_cb _hidl_cb) override;
+    Return<void> createGrallocDirectChannel(const hidl_handle& buffer, uint64_t size, createGrallocDirectChannel_cb _hidl_cb) override;
+    Return<void> createEventQueue(const sp<IEventQueueCallback> &callback, createEventQueue_cb _hidl_cb);
+
+private:
+    // Block until ::android::SensorManager is initialized.
+    ::android::SensorManager& getInternalManager();
+    sp<::android::Looper> getLooper();
+
+    std::mutex mInternalManagerMutex;
+    ::android::SensorManager* mInternalManager = nullptr; // does not own
+
+    std::mutex mLooperMutex;
+    sp<::android::Looper> mLooper;
+
+    JavaVM* mJavaVm;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_SENSORMANAGER_H
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
new file mode 100644
index 0000000..2f9e922
--- /dev/null
+++ b/services/sensorservice/hidl/utils.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+#include <sensors/convert.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::Sensor;
+using ::android::hardware::hidl_string;
+using ::android::hardware::sensors::V1_0::SensorInfo;
+
+SensorInfo convertSensor(const Sensor& src) {
+    SensorInfo dst;
+    const String8& name = src.getName();
+    const String8& vendor = src.getVendor();
+    dst.name = hidl_string{name.string(), name.size()};
+    dst.vendor = hidl_string{vendor.string(), vendor.size()};
+    dst.version = src.getVersion();
+    dst.sensorHandle = src.getHandle();
+    dst.type = static_cast<::android::hardware::sensors::V1_0::SensorType>(
+            src.getType());
+    // maxRange uses maxValue because ::android::Sensor wraps the
+    // internal sensor_t in this way.
+    dst.maxRange = src.getMaxValue();
+    dst.resolution = src.getResolution();
+    dst.power = src.getPowerUsage();
+    dst.minDelay = src.getMinDelay();
+    dst.fifoReservedEventCount = src.getFifoReservedEventCount();
+    dst.fifoMaxEventCount = src.getFifoMaxEventCount();
+    dst.typeAsString = src.getStringType();
+    dst.requiredPermission = src.getRequiredPermission();
+    dst.maxDelay = src.getMaxDelay();
+    dst.flags = src.getFlags();
+    return dst;
+}
+
+Result convertResult(status_t status) {
+    switch (status) {
+        case OK:
+            return Result::OK;
+        case NAME_NOT_FOUND:
+            return Result::NOT_EXIST;
+        case NO_MEMORY:
+            return Result::NO_MEMORY;
+        case NO_INIT:
+            return Result::NO_INIT;
+        case PERMISSION_DENIED:
+            return Result::PERMISSION_DENIED;
+        case BAD_VALUE:
+            return Result::BAD_VALUE;
+        case INVALID_OPERATION:
+            return Result::INVALID_OPERATION;
+        default:
+            return Result::UNKNOWN_ERROR;
+    }
+}
+
+::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
+    ::android::hardware::sensors::V1_0::Event dst;
+    ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
+            reinterpret_cast<const sensors_event_t&>(src), &dst);
+    return dst;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
diff --git a/services/sensorservice/hidl/utils.h b/services/sensorservice/hidl/utils.h
new file mode 100644
index 0000000..b350928
--- /dev/null
+++ b/services/sensorservice/hidl/utils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_UTILS_H
+#define ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_UTILS_H
+
+#include <android/frameworks/sensorservice/1.0/types.h>
+#include <android/hardware/sensors/1.0/types.h>
+#include <hidl/HidlSupport.h>
+#include <sensor/Sensor.h>
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace V1_0 {
+namespace implementation {
+
+::android::hardware::sensors::V1_0::SensorInfo convertSensor(const ::android::Sensor &src);
+Result convertResult(status_t status);
+
+::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent &event);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sensorservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_UTILS_H
diff --git a/services/sensorservice/mat.h b/services/sensorservice/mat.h
index a76fc91..495c14e 100644
--- a/services/sensorservice/mat.h
+++ b/services/sensorservice/mat.h
@@ -139,13 +139,13 @@
 
     mat() { }
     mat(const mat& rhs)  : base(rhs) { }
-    mat(const base& rhs) : base(rhs) { }
+    mat(const base& rhs) : base(rhs) { }  // NOLINT(implicit)
 
     // -----------------------------------------------------------------------
     // conversion constructors
 
     // sets the diagonal to the value, off-diagonal to zero
-    mat(pTYPE rhs) {
+    mat(pTYPE rhs) {  // NOLINT(implicit)
         helpers::doAssign(*this, rhs);
     }
 
@@ -220,7 +220,7 @@
     template<size_t PREV_COLUMN>
     struct column_builder {
         mat& matrix;
-        column_builder(mat& matrix) : matrix(matrix) { }
+        explicit column_builder(mat& matrix) : matrix(matrix) { }
     };
 
     // operator << is not a method of column_builder<> so we can
@@ -265,9 +265,9 @@
     enum { ROWS = R, COLS = 1 };
 
     mat() { }
-    mat(const base& rhs) : base(rhs) { }
+    explicit mat(const base& rhs) : base(rhs) { }
     mat(const mat& rhs) : base(rhs) { }
-    mat(const TYPE& rhs) { helpers::doAssign(*this, rhs); }
+    explicit mat(const TYPE& rhs) { helpers::doAssign(*this, rhs); }
     mat& operator=(const mat& rhs) { base::operator=(rhs); return *this; }
     mat& operator=(const base& rhs) { base::operator=(rhs); return *this; }
     mat& operator=(const TYPE& rhs) { return helpers::doAssign(*this, rhs); }
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
index 45296dd..e894655 100644
--- a/services/sensorservice/tests/Android.mk
+++ b/services/sensorservice/tests/Android.mk
@@ -5,7 +5,7 @@
 	sensorservicetest.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libcutils libutils libui libgui
+	libutils libsensor libandroid
 
 LOCAL_MODULE:= test-sensorservice
 
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 186b60c..1cb0489 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -16,9 +16,9 @@
 
 #include <inttypes.h>
 #include <android/sensor.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
 #include <utils/Looper.h>
 
 using namespace android;
diff --git a/services/sensorservice/vec.h b/services/sensorservice/vec.h
index a142bad..9e5d280 100644
--- a/services/sensorservice/vec.h
+++ b/services/sensorservice/vec.h
@@ -322,12 +322,12 @@
 
     vec() { }
     vec(const vec& rhs)  : base(rhs) { }
-    vec(const base& rhs) : base(rhs) { }
+    vec(const base& rhs) : base(rhs) { }  // NOLINT(implicit)
 
     // -----------------------------------------------------------------------
     // conversion constructors
 
-    vec(pTYPE rhs) {
+    vec(pTYPE rhs) {  // NOLINT(implicit)
         for (size_t i=0 ; i<SIZE ; i++)
             base::operator[](i) = rhs;
     }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
new file mode 100644
index 0000000..cc93105
--- /dev/null
+++ b/services/surfaceflinger/Android.bp
@@ -0,0 +1,4 @@
+cc_library_static {
+    name: "libsurfaceflingerincludes",
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index ffda035..95a522d 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -9,19 +9,23 @@
     DisplayDevice.cpp \
     DispSync.cpp \
     EventControlThread.cpp \
+    StartBootAnimThread.cpp \
     EventThread.cpp \
-    FenceTracker.cpp \
     FrameTracker.cpp \
     GpuService.cpp \
     Layer.cpp \
     LayerDim.cpp \
+    LayerRejecter.cpp \
+    LayerVector.cpp \
     MessageQueue.cpp \
     MonitoredProducer.cpp \
     SurfaceFlingerConsumer.cpp \
+    SurfaceInterceptor.cpp \
     Transform.cpp \
+    DisplayHardware/ComposerHal.cpp \
     DisplayHardware/FramebufferSurface.cpp \
     DisplayHardware/HWC2.cpp \
-    DisplayHardware/HWC2On1Adapter.cpp \
+    DisplayHardware/HWComposerBufferCache.cpp \
     DisplayHardware/PowerHAL.cpp \
     DisplayHardware/VirtualDisplaySurface.cpp \
     Effects/Daltonizer.cpp \
@@ -34,13 +38,13 @@
     RenderEngine/GLExtensions.cpp \
     RenderEngine/RenderEngine.cpp \
     RenderEngine/Texture.cpp \
-    RenderEngine/GLES10RenderEngine.cpp \
-    RenderEngine/GLES11RenderEngine.cpp \
-    RenderEngine/GLES20RenderEngine.cpp
+    RenderEngine/GLES20RenderEngine.cpp \
 
+LOCAL_MODULE := libsurfaceflinger
 LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include \
-	external/vulkan-validation-layers/libs/vkjson
+    frameworks/native/vulkan/include \
+    external/vulkan-validation-layers/libs/vkjson \
+    system/libhwbinder/fast_msgq/include \
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -56,82 +60,29 @@
         DisplayHardware/HWComposer_hwc1.cpp
 endif
 
-ifeq ($(TARGET_BOARD_PLATFORM),omap4)
-    LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-endif
-ifeq ($(TARGET_BOARD_PLATFORM),s5pc110)
-    LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-endif
-
-ifeq ($(TARGET_DISABLE_TRIPLE_BUFFERING),true)
-    LOCAL_CFLAGS += -DTARGET_DISABLE_TRIPLE_BUFFERING
-endif
-
-ifeq ($(TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS),true)
-    LOCAL_CFLAGS += -DFORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS
-endif
-
-ifneq ($(NUM_FRAMEBUFFER_SURFACE_BUFFERS),)
-    LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
-endif
-
-ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true)
-    LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
-endif
-
-# The following two BoardConfig variables define (respectively):
-#
-#   - The phase offset between hardware vsync and when apps are woken up by the
-#     Choreographer callback
-#   - The phase offset between hardware vsync and when SurfaceFlinger wakes up
-#     to consume input
-#
-# Their values can be tuned to trade off between display pipeline latency (both
-# overall latency and the lengths of the app --> SF and SF --> display phases)
-# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
-# while interacting with the device). The default values should produce a
-# relatively low amount of jitter at the expense of roughly two frames of
-# app --> display latency, and unless significant testing is performed to avoid
-# increased display jitter (both manual investigation using systrace [1] and
-# automated testing using dumpsys gfxinfo [2] are recommended), they should not
-# be modified.
-#
-# [1] https://developer.android.com/studio/profile/systrace.html
-# [2] https://developer.android.com/training/testing/performance.html
-
-ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
-    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
-else
-    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=1000000
-endif
-
-ifneq ($(SF_VSYNC_EVENT_PHASE_OFFSET_NS),)
-    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=$(SF_VSYNC_EVENT_PHASE_OFFSET_NS)
-else
-    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=1000000
-endif
-
-ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
-    LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS)
-else
-    LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=0
-endif
-
-ifneq ($(MAX_VIRTUAL_DISPLAY_DIMENSION),)
-    LOCAL_CFLAGS += -DMAX_VIRTUAL_DISPLAY_DIMENSION=$(MAX_VIRTUAL_DISPLAY_DIMENSION)
-else
-    LOCAL_CFLAGS += -DMAX_VIRTUAL_DISPLAY_DIMENSION=0
-endif
-
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
-LOCAL_CFLAGS += -std=c++14
 
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := \
+    libhwcomposer-command-buffer \
+    libtrace_proto \
+    libvkjson \
+    libvr_manager \
+    libvrflinger
+
 LOCAL_SHARED_LIBRARIES := \
+    android.frameworks.vr.composer@1.0 \
+    android.hardware.graphics.allocator@2.0 \
+    android.hardware.graphics.composer@2.1 \
+    android.hardware.configstore@1.0 \
+    android.hardware.configstore-utils \
     libcutils \
     liblog \
     libdl \
+    libfmq \
     libhardware \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     libutils \
     libEGL \
     libGLESv1_CM \
@@ -140,9 +91,18 @@
     libui \
     libgui \
     libpowermanager \
-    libvulkan
+    libvulkan \
+    libsync \
+    libprotobuf-cpp-lite \
+    libbase \
+    android.hardware.power@1.0
 
-LOCAL_MODULE := libsurfaceflinger
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+    android.hardware.graphics.allocator@2.0 \
+    android.hardware.graphics.composer@2.1 \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
@@ -154,16 +114,12 @@
 
 LOCAL_CLANG := true
 
-LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
-LOCAL_CPPFLAGS := -std=c++14
 
 LOCAL_INIT_RC := surfaceflinger.rc
 
-ifneq ($(ENABLE_CPUSETS),)
-    LOCAL_CFLAGS += -DENABLE_CPUSETS
-endif
-
 ifeq ($(TARGET_USES_HWC2),true)
     LOCAL_CFLAGS += -DUSE_HWC2
 endif
@@ -172,14 +128,24 @@
     main_surfaceflinger.cpp
 
 LOCAL_SHARED_LIBRARIES := \
+    android.frameworks.displayservice@1.0 \
+    android.hardware.configstore@1.0 \
+    android.hardware.configstore-utils \
+    android.hardware.graphics.allocator@2.0 \
     libsurfaceflinger \
     libcutils \
+    libdisplayservicehidl \
     liblog \
     libbinder \
+    libhidlbase \
+    libhidltransport \
     libutils \
+    libui \
+    libgui \
     libdl
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
 
 LOCAL_MODULE := surfaceflinger
 
@@ -199,7 +165,6 @@
 LOCAL_CLANG := true
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
-LOCAL_CPPFLAGS := -std=c++14
 
 LOCAL_SRC_FILES := \
     DdmConnection.cpp
@@ -215,3 +180,5 @@
 
 include $(BUILD_SHARED_LIBRARY)
 endif # libnativehelper
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index e14a59b..8ba6cb9 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -35,7 +35,13 @@
 // ---------------------------------------------------------------------------
 
 Client::Client(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger)
+    : Client(flinger, nullptr)
+{
+}
+
+Client::Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer)
+    : mFlinger(flinger),
+      mParentLayer(parentLayer)
 {
 }
 
@@ -43,10 +49,27 @@
 {
     const size_t count = mLayers.size();
     for (size_t i=0 ; i<count ; i++) {
-        mFlinger->removeLayer(mLayers.valueAt(i));
+        sp<Layer> l = mLayers.valueAt(i).promote();
+        if (l != nullptr) {
+            mFlinger->removeLayer(l);
+        }
     }
 }
 
+void Client::setParentLayer(const sp<Layer>& parentLayer) {
+    Mutex::Autolock _l(mLock);
+    mParentLayer = parentLayer;
+}
+
+sp<Layer> Client::getParentLayer(bool* outParentDied) const {
+    Mutex::Autolock _l(mLock);
+    sp<Layer> parent = mParentLayer.promote();
+    if (outParentDied != nullptr) {
+        *outParentDied = (mParentLayer != nullptr && parent == nullptr);
+    }
+    return parent;
+}
+
 status_t Client::initCheck() const {
     return NO_ERROR;
 }
@@ -90,12 +113,17 @@
      const int pid = ipc->getCallingPid();
      const int uid = ipc->getCallingUid();
      const int self_pid = getpid();
-     if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)) {
+     // If we are called from another non root process without the GRAPHICS, SYSTEM, or ROOT
+     // uid we require the sAccessSurfaceFlinger permission.
+     // We grant an exception in the case that the Client has a "parent layer", as its
+     // effects will be scoped to that layer.
+     if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)
+             && (getParentLayer() == nullptr)) {
          // we're called from a different process, do the real check
          if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
          {
              ALOGE("Permission Denial: "
-                     "can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
+                     "can't openGlobalTransaction pid=%d, uid<=%d", pid, uid);
              return PERMISSION_DENIED;
          }
      }
@@ -106,14 +134,31 @@
 status_t Client::createSurface(
         const String8& name,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
+        const sp<IBinder>& parentHandle, uint32_t windowType, uint32_t ownerUid,
         sp<IBinder>* handle,
         sp<IGraphicBufferProducer>* gbp)
 {
+    sp<Layer> parent = nullptr;
+    if (parentHandle != nullptr) {
+        parent = getLayerUser(parentHandle);
+        if (parent == nullptr) {
+            return NAME_NOT_FOUND;
+        }
+    }
+    if (parent == nullptr) {
+        bool parentDied;
+        parent = getParentLayer(&parentDied);
+        // If we had a parent, but it died, we've lost all
+        // our capabilities.
+        if (parentDied) {
+            return NAME_NOT_FOUND;
+        }
+    }
+
     /*
      * createSurface must be called from the GL thread so that it can
      * have access to the GL context.
      */
-
     class MessageCreateLayer : public MessageBase {
         SurfaceFlinger* flinger;
         Client* client;
@@ -124,26 +169,32 @@
         uint32_t w, h;
         PixelFormat format;
         uint32_t flags;
+        sp<Layer>* parent;
+        uint32_t windowType;
+        uint32_t ownerUid;
     public:
         MessageCreateLayer(SurfaceFlinger* flinger,
                 const String8& name, Client* client,
                 uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-                sp<IBinder>* handle,
-                sp<IGraphicBufferProducer>* gbp)
+                sp<IBinder>* handle, uint32_t windowType, uint32_t ownerUid,
+                sp<IGraphicBufferProducer>* gbp,
+                sp<Layer>* parent)
             : flinger(flinger), client(client),
               handle(handle), gbp(gbp), result(NO_ERROR),
-              name(name), w(w), h(h), format(format), flags(flags) {
+              name(name), w(w), h(h), format(format), flags(flags),
+              parent(parent), windowType(windowType), ownerUid(ownerUid) {
         }
         status_t getResult() const { return result; }
         virtual bool handler() {
             result = flinger->createLayer(name, client, w, h, format, flags,
-                    handle, gbp);
+                    windowType, ownerUid, handle, gbp, parent);
             return true;
         }
     };
 
     sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
-            name, this, w, h, format, flags, handle, gbp);
+            name, this, w, h, format, flags, handle,
+            windowType, ownerUid, gbp, &parent);
     mFlinger->postMessageSync(msg);
     return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
 }
@@ -170,15 +221,5 @@
     return NO_ERROR;
 }
 
-status_t Client::getTransformToDisplayInverse(const sp<IBinder>& handle,
-        bool* outTransformToDisplayInverse) const {
-    sp<Layer> layer = getLayerUser(handle);
-    if (layer == NULL) {
-        return NAME_NOT_FOUND;
-    }
-    *outTransformToDisplayInverse = layer->getTransformToDisplayInverse();
-    return NO_ERROR;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 12db505..2aab28f 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -38,8 +38,9 @@
 class Client : public BnSurfaceComposerClient
 {
 public:
-        Client(const sp<SurfaceFlinger>& flinger);
-        ~Client();
+    explicit Client(const sp<SurfaceFlinger>& flinger);
+    Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer);
+    ~Client();
 
     status_t initCheck() const;
 
@@ -50,11 +51,14 @@
 
     sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
 
+    void setParentLayer(const sp<Layer>& parentLayer);
+
 private:
     // ISurfaceComposerClient interface
     virtual status_t createSurface(
             const String8& name,
             uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
+            const sp<IBinder>& parent, uint32_t windowType, uint32_t ownerUid,
             sp<IBinder>* handle,
             sp<IGraphicBufferProducer>* gbp);
 
@@ -63,17 +67,18 @@
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
-    virtual status_t getTransformToDisplayInverse(
-            const sp<IBinder>& handle, bool* outTransformToDisplayInverse) const;
 
     virtual status_t onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
 
+    sp<Layer> getParentLayer(bool* outParentDied = nullptr) const;
+
     // constant
     sp<SurfaceFlinger> mFlinger;
 
     // protected by mLock
     DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
+    wp<Layer> mParentLayer;
 
     // thread-safe
     mutable Mutex mLock;
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index 6524481..d56b1c8 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H
 #define ANDROID_SURFACE_FLINGER_COLORIZER_H
 
+#include <utils/String8.h>
+
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -34,7 +36,7 @@
         WHITE   = 37
     };
 
-    Colorizer(bool enabled)
+    explicit Colorizer(bool enabled)
         : mEnabled(enabled) {
     }
 
diff --git a/services/surfaceflinger/DdmConnection.cpp b/services/surfaceflinger/DdmConnection.cpp
index 659c2c8..35d55f5 100644
--- a/services/surfaceflinger/DdmConnection.cpp
+++ b/services/surfaceflinger/DdmConnection.cpp
@@ -15,8 +15,10 @@
  */
 
 #include <dlfcn.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "jni.h"
 #include "DdmConnection.h"
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
index b6b088b..938d14b 100644
--- a/services/surfaceflinger/DdmConnection.h
+++ b/services/surfaceflinger/DdmConnection.h
@@ -24,6 +24,9 @@
 
 class DdmConnection {
 public:
+    // Creates a JVM and registers all handlers to DDMS.
+    // This allows tools relying on DDMS to find surfaceflinger
+    // (e.g: Memory Leak finder, heap analyzer, ...)
     static void start(const char* name);
 };
 
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 1a9820d..bd9b8aa 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -22,19 +22,19 @@
 
 #include <math.h>
 
-#include <cutils/log.h>
+#include <algorithm>
 
-#include <ui/Fence.h>
-
+#include <log/log.h>
 #include <utils/String8.h>
 #include <utils/Thread.h>
 #include <utils/Trace.h>
 #include <utils/Vector.h>
 
-#include "DispSync.h"
-#include "EventLog/EventLog.h"
+#include <ui/Fence.h>
 
-#include <algorithm>
+#include "DispSync.h"
+#include "SurfaceFlinger.h"
+#include "EventLog/EventLog.h"
 
 using std::max;
 using std::min;
@@ -55,16 +55,12 @@
 // present time and the nearest software-predicted vsync.
 static const nsecs_t kErrorThreshold = 160000000000;    // 400 usec squared
 
-// This is the offset from the present fence timestamps to the corresponding
-// vsync event.
-static const int64_t kPresentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
-
 #undef LOG_TAG
 #define LOG_TAG "DispSyncThread"
 class DispSyncThread: public Thread {
 public:
 
-    DispSyncThread(const char* name):
+    explicit DispSyncThread(const char* name):
             mName(name),
             mStop(false),
             mPeriod(0),
@@ -220,7 +216,8 @@
         return BAD_VALUE;
     }
 
-    // This method is only here to handle the kIgnorePresentFences case.
+    // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
+    // case.
     bool hasAnyEventListeners() {
         if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
@@ -380,8 +377,10 @@
 DispSync::DispSync(const char* name) :
         mName(name),
         mRefreshSkipCount(0),
-        mThread(new DispSyncThread(name)) {
+        mThread(new DispSyncThread(name)),
+        mIgnorePresentFences(!SurfaceFlinger::hasSyncFramework){
 
+    mPresentTimeOffset = SurfaceFlinger::dispSyncPresentTimeOffset;
     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
     // set DispSync to SCHED_FIFO to minimize jitter
     struct sched_param param = {0};
@@ -400,7 +399,7 @@
         // Even if we're just ignoring the fences, the zero-phase tracing is
         // not needed because any time there is an event registered we will
         // turn on the HW vsync events.
-        if (!kIgnorePresentFences && kEnableZeroPhaseTracer) {
+        if (!mIgnorePresentFences && kEnableZeroPhaseTracer) {
             addEventListener("ZeroPhaseTracer", 0, new ZeroPhaseTracer());
         }
     }
@@ -434,7 +433,7 @@
             nsecs_t t = f->getSignalTime();
             if (t < INT64_MAX) {
                 mPresentFences[i].clear();
-                mPresentTimes[i] = t + kPresentTimeOffset;
+                mPresentTimes[i] = t + mPresentTimeOffset;
             }
         }
     }
@@ -479,7 +478,7 @@
         resetErrorLocked();
     }
 
-    if (kIgnorePresentFences) {
+    if (mIgnorePresentFences) {
         // If we don't have the sync framework we will never have
         // addPresentFence called.  This means we have no way to know whether
         // or not we're synchronized with the HW vsyncs, so we just request
@@ -644,7 +643,7 @@
 void DispSync::dump(String8& result) const {
     Mutex::Autolock lock(mMutex);
     result.appendFormat("present fences are %s\n",
-            kIgnorePresentFences ? "ignored" : "used");
+            mIgnorePresentFences ? "ignored" : "used");
     result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n",
             mPeriod, 1000000000.0 / mPeriod, mRefreshSkipCount);
     result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 537c81b..82ae795 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -25,15 +25,6 @@
 
 namespace android {
 
-// Ignore present (retire) fences if the device doesn't have support for the
-// sync framework
-#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK)
-static const bool kIgnorePresentFences = true;
-#else
-static const bool kIgnorePresentFences = false;
-#endif
-
-
 class String8;
 class Fence;
 class DispSyncThread;
@@ -61,7 +52,7 @@
         virtual void onDispSyncEvent(nsecs_t when) = 0;
     };
 
-    DispSync(const char* name);
+    explicit DispSync(const char* name);
     ~DispSync();
 
     // reset clears the resync samples and error value.
@@ -182,6 +173,14 @@
 
     // mMutex is used to protect access to all member variables.
     mutable Mutex mMutex;
+
+    // This is the offset from the present fence timestamps to the corresponding
+    // vsync event.
+    int64_t mPresentTimeOffset;
+
+    // Ignore present (retire) fences if the device doesn't have support for the
+    // sync framework
+    bool mIgnorePresentFences;
 };
 
 }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5c2c0ad..b5ffc60 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -47,6 +47,9 @@
 #include "SurfaceFlinger.h"
 #include "Layer.h"
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 // ----------------------------------------------------------------------------
 using namespace android;
 // ----------------------------------------------------------------------------
@@ -57,6 +60,13 @@
 static constexpr bool kEGLAndroidSwapRectangle = false;
 #endif
 
+// retrieve triple buffer setting from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs,
+        &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3;
+
 #if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle)
 // Dummy implementation in case it is missing.
 inline void eglSetSwapRectangleANDROID (EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint) {
@@ -70,6 +80,7 @@
 
 uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
 
+// clang-format off
 DisplayDevice::DisplayDevice(
         const sp<SurfaceFlinger>& flinger,
         DisplayType type,
@@ -81,7 +92,8 @@
         const wp<IBinder>& displayToken,
         const sp<DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer,
-        EGLConfig config)
+        EGLConfig config,
+        bool supportWideColor)
     : lastCompositionHadVisibleLayers(false),
       mFlinger(flinger),
       mType(type),
@@ -103,10 +115,17 @@
       mPowerMode(HWC_POWER_MODE_OFF),
       mActiveConfig(0)
 {
+    // clang-format on
     Surface* surface;
     mNativeWindow = surface = new Surface(producer, false);
     ANativeWindow* const window = mNativeWindow.get();
 
+#ifdef USE_HWC2
+    mActiveColorMode = static_cast<android_color_mode_t>(-1);
+    mDisplayHasWideColor = supportWideColor;
+#else
+    (void) supportWideColor;
+#endif
     /*
      * Create our display's surface
      */
@@ -165,9 +184,9 @@
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
 
-#ifdef NUM_FRAMEBUFFER_SURFACE_BUFFERS
-    surface->allocateBuffers();
-#endif
+    if (useTripleFramebuffer) {
+        surface->allocateBuffers();
+    }
 }
 
 DisplayDevice::~DisplayDevice() {
@@ -613,3 +632,17 @@
     mDisplaySurface->dumpAsString(surfaceDump);
     result.append(surfaceDump);
 }
+
+std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
+
+DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure)
+    : type(type),
+      layerStack(DisplayDevice::NO_LAYER_STACK),
+      orientation(0),
+      width(0),
+      height(0),
+      isSecure(isSecure)
+{
+    viewport.makeInvalid();
+    frame.makeInvalid();
+}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 105e980..e2852a7 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -83,6 +83,7 @@
         NO_LAYER_STACK = 0xFFFFFFFF,
     };
 
+    // clang-format off
     DisplayDevice(
             const sp<SurfaceFlinger>& flinger,
             DisplayType type,
@@ -94,7 +95,9 @@
             const wp<IBinder>& displayToken,
             const sp<DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer,
-            EGLConfig config);
+            EGLConfig config,
+            bool supportWideColor);
+    // clang-format on
 
     ~DisplayDevice();
 
@@ -146,6 +149,7 @@
     status_t beginFrame(bool mustRecompose) const;
 #ifdef USE_HWC2
     status_t prepareFrame(HWComposer& hwc);
+    bool getWideColorSupport() const { return mDisplayHasWideColor; }
 #else
     status_t prepareFrame(const HWComposer& hwc) const;
 #endif
@@ -242,7 +246,11 @@
     static status_t orientationToTransfrom(int orientation,
             int w, int h, Transform* tr);
 
+    // The identifier of the active layer stack for this display. Several displays
+    // can use the same layer stack: A z-ordered group of layers (sometimes called
+    // "surfaces"). Any given layer can only be on a single layer stack.
     uint32_t mLayerStack;
+
     int mOrientation;
     static uint32_t sPrimaryDisplayOrientation;
     // user-provided visible area of the layer stack
@@ -260,9 +268,36 @@
 #ifdef USE_HWC2
     // current active color mode
     android_color_mode_t mActiveColorMode;
+
+    // Need to know if display is wide-color capable or not.
+    // Initialized by SurfaceFlinger when the DisplayDevice is created.
+    // Fed to RenderEngine during composition.
+    bool mDisplayHasWideColor;
 #endif
 };
 
+struct DisplayDeviceState {
+    DisplayDeviceState() = default;
+    DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
+
+    bool isValid() const { return type >= 0; }
+    bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
+    bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
+
+    static std::atomic<int32_t> nextDisplayId;
+    int32_t displayId = nextDisplayId++;
+    DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID;
+    sp<IGraphicBufferProducer> surface;
+    uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+    Rect viewport;
+    Rect frame;
+    uint8_t orientation = 0;
+    uint32_t width = 0;
+    uint32_t height = 0;
+    String8 displayName;
+    bool isSecure = false;
+};
+
 }; // namespace android
 
 #endif // ANDROID_DISPLAY_DEVICE_H
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
new file mode 100644
index 0000000..9d16044
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,1102 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <gui/BufferQueue.h>
+
+#include "ComposerHal.h"
+
+namespace android {
+
+using hardware::Return;
+using hardware::hidl_vec;
+using hardware::hidl_handle;
+
+namespace Hwc2 {
+
+namespace {
+
+class BufferHandle {
+public:
+    BufferHandle(const native_handle_t* buffer)
+    {
+        // nullptr is not a valid handle to HIDL
+        mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+    }
+
+    operator const hidl_handle&() const
+    {
+        return mHandle;
+    }
+
+private:
+    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
+    hidl_handle mHandle;
+};
+
+class FenceHandle
+{
+public:
+    FenceHandle(int fd, bool owned)
+        : mOwned(owned)
+    {
+        native_handle_t* handle;
+        if (fd >= 0) {
+            handle = native_handle_init(mStorage, 1, 0);
+            handle->data[0] = fd;
+        } else {
+            // nullptr is not a valid handle to HIDL
+            handle = native_handle_init(mStorage, 0, 0);
+        }
+        mHandle = handle;
+    }
+
+    ~FenceHandle()
+    {
+        if (mOwned) {
+            native_handle_close(mHandle);
+        }
+    }
+
+    operator const hidl_handle&() const
+    {
+        return mHandle;
+    }
+
+private:
+    bool mOwned;
+    NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
+    hidl_handle mHandle;
+};
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+template<typename T, typename U>
+T unwrapRet(Return<T>& ret, const U& default_val)
+{
+    return (ret.isOk()) ? static_cast<T>(ret) :
+        static_cast<T>(default_val);
+}
+
+Error unwrapRet(Return<Error>& ret)
+{
+    return unwrapRet(ret, kDefaultError);
+}
+
+} // anonymous namespace
+
+Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
+    : CommandWriterBase(initialMaxSize) {}
+
+Composer::CommandWriter::~CommandWriter()
+{
+}
+
+void Composer::CommandWriter::setLayerInfo(uint32_t type, uint32_t appId)
+{
+    constexpr uint16_t kSetLayerInfoLength = 2;
+    beginCommand(
+        static_cast<IComposerClient::Command>(
+            IVrComposerClient::VrCommand::SET_LAYER_INFO),
+        kSetLayerInfoLength);
+    write(type);
+    write(appId);
+    endCommand();
+}
+
+void Composer::CommandWriter::setClientTargetMetadata(
+        const IVrComposerClient::BufferMetadata& metadata)
+{
+    constexpr uint16_t kSetClientTargetMetadataLength = 7;
+    beginCommand(
+        static_cast<IComposerClient::Command>(
+            IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA),
+        kSetClientTargetMetadataLength);
+    writeBufferMetadata(metadata);
+    endCommand();
+}
+
+void Composer::CommandWriter::setLayerBufferMetadata(
+        const IVrComposerClient::BufferMetadata& metadata)
+{
+    constexpr uint16_t kSetLayerBufferMetadataLength = 7;
+    beginCommand(
+        static_cast<IComposerClient::Command>(
+            IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA),
+        kSetLayerBufferMetadataLength);
+    writeBufferMetadata(metadata);
+    endCommand();
+}
+
+void Composer::CommandWriter::writeBufferMetadata(
+        const IVrComposerClient::BufferMetadata& metadata)
+{
+    write(metadata.width);
+    write(metadata.height);
+    write(metadata.stride);
+    write(metadata.layerCount);
+    writeSigned(static_cast<int32_t>(metadata.format));
+    write64(metadata.usage);
+}
+
+Composer::Composer(bool useVrComposer)
+    : mWriter(kWriterInitialSize),
+      mIsUsingVrComposer(useVrComposer)
+{
+    if (mIsUsingVrComposer) {
+        mComposer = IComposer::getService("vr");
+    } else {
+        mComposer = IComposer::getService(); // use default name
+    }
+
+    if (mComposer == nullptr) {
+        LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+    }
+
+    mComposer->createClient(
+            [&](const auto& tmpError, const auto& tmpClient)
+            {
+                if (tmpError == Error::NONE) {
+                    mClient = tmpClient;
+                }
+            });
+    if (mClient == nullptr) {
+        LOG_ALWAYS_FATAL("failed to create composer client");
+    }
+}
+
+std::vector<IComposer::Capability> Composer::getCapabilities()
+{
+    std::vector<IComposer::Capability> capabilities;
+    mComposer->getCapabilities(
+            [&](const auto& tmpCapabilities) {
+                capabilities = tmpCapabilities;
+            });
+
+    return capabilities;
+}
+
+std::string Composer::dumpDebugInfo()
+{
+    std::string info;
+    mComposer->dumpDebugInfo([&](const auto& tmpInfo) {
+        info = tmpInfo.c_str();
+    });
+
+    return info;
+}
+
+void Composer::registerCallback(const sp<IComposerCallback>& callback)
+{
+    auto ret = mClient->registerCallback(callback);
+    if (!ret.isOk()) {
+        ALOGE("failed to register IComposerCallback");
+    }
+}
+
+void Composer::resetCommands() {
+    mWriter.reset();
+}
+
+uint32_t Composer::getMaxVirtualDisplayCount()
+{
+    auto ret = mClient->getMaxVirtualDisplayCount();
+    return unwrapRet(ret, 0);
+}
+
+Error Composer::createVirtualDisplay(uint32_t width, uint32_t height,
+            PixelFormat* format, Display* outDisplay)
+{
+    const uint32_t bufferSlotCount = 1;
+    Error error = kDefaultError;
+    mClient->createVirtualDisplay(width, height, *format, bufferSlotCount,
+            [&](const auto& tmpError, const auto& tmpDisplay,
+                const auto& tmpFormat) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outDisplay = tmpDisplay;
+                *format = tmpFormat;
+            });
+
+    return error;
+}
+
+Error Composer::destroyVirtualDisplay(Display display)
+{
+    auto ret = mClient->destroyVirtualDisplay(display);
+    return unwrapRet(ret);
+}
+
+Error Composer::acceptDisplayChanges(Display display)
+{
+    mWriter.selectDisplay(display);
+    mWriter.acceptDisplayChanges();
+    return Error::NONE;
+}
+
+Error Composer::createLayer(Display display, Layer* outLayer)
+{
+    Error error = kDefaultError;
+    mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
+            [&](const auto& tmpError, const auto& tmpLayer) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outLayer = tmpLayer;
+            });
+
+    return error;
+}
+
+Error Composer::destroyLayer(Display display, Layer layer)
+{
+    auto ret = mClient->destroyLayer(display, layer);
+    return unwrapRet(ret);
+}
+
+Error Composer::getActiveConfig(Display display, Config* outConfig)
+{
+    Error error = kDefaultError;
+    mClient->getActiveConfig(display,
+            [&](const auto& tmpError, const auto& tmpConfig) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outConfig = tmpConfig;
+            });
+
+    return error;
+}
+
+Error Composer::getChangedCompositionTypes(Display display,
+        std::vector<Layer>* outLayers,
+        std::vector<IComposerClient::Composition>* outTypes)
+{
+    mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+    return Error::NONE;
+}
+
+Error Composer::getColorModes(Display display,
+        std::vector<ColorMode>* outModes)
+{
+    Error error = kDefaultError;
+    mClient->getColorModes(display,
+            [&](const auto& tmpError, const auto& tmpModes) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outModes = tmpModes;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayAttribute(Display display, Config config,
+        IComposerClient::Attribute attribute, int32_t* outValue)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayAttribute(display, config, attribute,
+            [&](const auto& tmpError, const auto& tmpValue) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outValue = tmpValue;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayConfigs(Display display,
+        std::vector<Config>* outConfigs)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayConfigs(display,
+            [&](const auto& tmpError, const auto& tmpConfigs) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outConfigs = tmpConfigs;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayName(Display display, std::string* outName)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayName(display,
+            [&](const auto& tmpError, const auto& tmpName) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outName = tmpName.c_str();
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayRequests(Display display,
+        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+        std::vector<uint32_t>* outLayerRequestMasks)
+{
+    mReader.takeDisplayRequests(display, outDisplayRequestMask,
+            outLayers, outLayerRequestMasks);
+    return Error::NONE;
+}
+
+Error Composer::getDisplayType(Display display,
+        IComposerClient::DisplayType* outType)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayType(display,
+            [&](const auto& tmpError, const auto& tmpType) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outType = tmpType;
+            });
+
+    return error;
+}
+
+Error Composer::getDozeSupport(Display display, bool* outSupport)
+{
+    Error error = kDefaultError;
+    mClient->getDozeSupport(display,
+            [&](const auto& tmpError, const auto& tmpSupport) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outSupport = tmpSupport;
+            });
+
+    return error;
+}
+
+Error Composer::getHdrCapabilities(Display display,
+        std::vector<Hdr>* outTypes, float* outMaxLuminance,
+        float* outMaxAverageLuminance, float* outMinLuminance)
+{
+    Error error = kDefaultError;
+    mClient->getHdrCapabilities(display,
+            [&](const auto& tmpError, const auto& tmpTypes,
+                const auto& tmpMaxLuminance,
+                const auto& tmpMaxAverageLuminance,
+                const auto& tmpMinLuminance) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outTypes = tmpTypes;
+                *outMaxLuminance = tmpMaxLuminance;
+                *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                *outMinLuminance = tmpMinLuminance;
+            });
+
+    return error;
+}
+
+Error Composer::getReleaseFences(Display display,
+        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+    mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+    return Error::NONE;
+}
+
+Error Composer::presentDisplay(Display display, int* outPresentFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.presentDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.takePresentFence(display, outPresentFence);
+
+    return Error::NONE;
+}
+
+Error Composer::setActiveConfig(Display display, Config config)
+{
+    auto ret = mClient->setActiveConfig(display, config);
+    return unwrapRet(ret);
+}
+
+Error Composer::setClientTarget(Display display, uint32_t slot,
+        const sp<GraphicBuffer>& target,
+        int acquireFence, Dataspace dataspace,
+        const std::vector<IComposerClient::Rect>& damage)
+{
+    mWriter.selectDisplay(display);
+    if (mIsUsingVrComposer && target.get()) {
+        IVrComposerClient::BufferMetadata metadata = {
+            .width = target->getWidth(),
+            .height = target->getHeight(),
+            .stride = target->getStride(),
+            .layerCount = target->getLayerCount(),
+            .format = static_cast<PixelFormat>(target->getPixelFormat()),
+            .usage = target->getUsage(),
+        };
+        mWriter.setClientTargetMetadata(metadata);
+    }
+
+    const native_handle_t* handle = nullptr;
+    if (target.get()) {
+        handle = target->getNativeBuffer()->handle;
+    }
+
+    mWriter.setClientTarget(slot, handle, acquireFence, dataspace, damage);
+    return Error::NONE;
+}
+
+Error Composer::setColorMode(Display display, ColorMode mode)
+{
+    auto ret = mClient->setColorMode(display, mode);
+    return unwrapRet(ret);
+}
+
+Error Composer::setColorTransform(Display display, const float* matrix,
+        ColorTransform hint)
+{
+    mWriter.selectDisplay(display);
+    mWriter.setColorTransform(matrix, hint);
+    return Error::NONE;
+}
+
+Error Composer::setOutputBuffer(Display display, const native_handle_t* buffer,
+        int releaseFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
+    return Error::NONE;
+}
+
+Error Composer::setPowerMode(Display display, IComposerClient::PowerMode mode)
+{
+    auto ret = mClient->setPowerMode(display, mode);
+    return unwrapRet(ret);
+}
+
+Error Composer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled)
+{
+    auto ret = mClient->setVsyncEnabled(display, enabled);
+    return unwrapRet(ret);
+}
+
+Error Composer::setClientTargetSlotCount(Display display)
+{
+    const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
+    auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
+    return unwrapRet(ret);
+}
+
+Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
+        uint32_t* outNumRequests)
+{
+    mWriter.selectDisplay(display);
+    mWriter.validateDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.hasChanges(display, outNumTypes, outNumRequests);
+
+    return Error::NONE;
+}
+
+Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                               uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
+   mWriter.selectDisplay(display);
+   mWriter.presentOrvalidateDisplay();
+
+   Error error = execute();
+   if (error != Error::NONE) {
+       return error;
+   }
+
+   mReader.takePresentOrValidateStage(display, state);
+
+   if (*state == 1) { // Present succeeded
+       mReader.takePresentFence(display, outPresentFence);
+   }
+
+   if (*state == 0) { // Validate succeeded.
+       mReader.hasChanges(display, outNumTypes, outNumRequests);
+   }
+
+   return Error::NONE;
+}
+
+Error Composer::setCursorPosition(Display display, Layer layer,
+        int32_t x, int32_t y)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerCursorPosition(x, y);
+    return Error::NONE;
+}
+
+Error Composer::setLayerBuffer(Display display, Layer layer,
+        uint32_t slot, const sp<GraphicBuffer>& buffer, int acquireFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    if (mIsUsingVrComposer && buffer.get()) {
+        IVrComposerClient::BufferMetadata metadata = {
+            .width = buffer->getWidth(),
+            .height = buffer->getHeight(),
+            .stride = buffer->getStride(),
+            .layerCount = buffer->getLayerCount(),
+            .format = static_cast<PixelFormat>(buffer->getPixelFormat()),
+            .usage = buffer->getUsage(),
+        };
+        mWriter.setLayerBufferMetadata(metadata);
+    }
+
+    const native_handle_t* handle = nullptr;
+    if (buffer.get()) {
+        handle = buffer->getNativeBuffer()->handle;
+    }
+
+    mWriter.setLayerBuffer(slot, handle, acquireFence);
+    return Error::NONE;
+}
+
+Error Composer::setLayerSurfaceDamage(Display display, Layer layer,
+        const std::vector<IComposerClient::Rect>& damage)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSurfaceDamage(damage);
+    return Error::NONE;
+}
+
+Error Composer::setLayerBlendMode(Display display, Layer layer,
+        IComposerClient::BlendMode mode)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerBlendMode(mode);
+    return Error::NONE;
+}
+
+Error Composer::setLayerColor(Display display, Layer layer,
+        const IComposerClient::Color& color)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerColor(color);
+    return Error::NONE;
+}
+
+Error Composer::setLayerCompositionType(Display display, Layer layer,
+        IComposerClient::Composition type)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerCompositionType(type);
+    return Error::NONE;
+}
+
+Error Composer::setLayerDataspace(Display display, Layer layer,
+        Dataspace dataspace)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerDataspace(dataspace);
+    return Error::NONE;
+}
+
+Error Composer::setLayerDisplayFrame(Display display, Layer layer,
+        const IComposerClient::Rect& frame)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerDisplayFrame(frame);
+    return Error::NONE;
+}
+
+Error Composer::setLayerPlaneAlpha(Display display, Layer layer,
+        float alpha)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerPlaneAlpha(alpha);
+    return Error::NONE;
+}
+
+Error Composer::setLayerSidebandStream(Display display, Layer layer,
+        const native_handle_t* stream)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSidebandStream(stream);
+    return Error::NONE;
+}
+
+Error Composer::setLayerSourceCrop(Display display, Layer layer,
+        const IComposerClient::FRect& crop)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerSourceCrop(crop);
+    return Error::NONE;
+}
+
+Error Composer::setLayerTransform(Display display, Layer layer,
+        Transform transform)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerTransform(transform);
+    return Error::NONE;
+}
+
+Error Composer::setLayerVisibleRegion(Display display, Layer layer,
+        const std::vector<IComposerClient::Rect>& visible)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerVisibleRegion(visible);
+    return Error::NONE;
+}
+
+Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerZOrder(z);
+    return Error::NONE;
+}
+
+Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
+                             uint32_t appId)
+{
+    if (mIsUsingVrComposer) {
+        mWriter.selectDisplay(display);
+        mWriter.selectLayer(layer);
+        mWriter.setLayerInfo(type, appId);
+    }
+    return Error::NONE;
+}
+
+Error Composer::execute()
+{
+    // prepare input command queue
+    bool queueChanged = false;
+    uint32_t commandLength = 0;
+    hidl_vec<hidl_handle> commandHandles;
+    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
+        mWriter.reset();
+        return Error::NO_RESOURCES;
+    }
+
+    // set up new input command queue if necessary
+    if (queueChanged) {
+        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
+        auto error = unwrapRet(ret);
+        if (error != Error::NONE) {
+            mWriter.reset();
+            return error;
+        }
+    }
+
+    Error error = kDefaultError;
+    mClient->executeCommands(commandLength, commandHandles,
+            [&](const auto& tmpError, const auto& tmpOutChanged,
+                const auto& tmpOutLength, const auto& tmpOutHandles)
+            {
+                error = tmpError;
+
+                // set up new output command queue if necessary
+                if (error == Error::NONE && tmpOutChanged) {
+                    error = kDefaultError;
+                    mClient->getOutputCommandQueue(
+                            [&](const auto& tmpError,
+                                const auto& tmpDescriptor)
+                            {
+                                error = tmpError;
+                                if (error != Error::NONE) {
+                                    return;
+                                }
+
+                                mReader.setMQDescriptor(tmpDescriptor);
+                            });
+                }
+
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
+                    error = mReader.parse();
+                    mReader.reset();
+                } else {
+                    error = Error::NO_RESOURCES;
+                }
+            });
+
+    if (error == Error::NONE) {
+        std::vector<CommandReader::CommandError> commandErrors =
+            mReader.takeErrors();
+
+        for (const auto& cmdErr : commandErrors) {
+            auto command = mWriter.getCommand(cmdErr.location);
+
+            if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
+                error = cmdErr.error;
+            } else {
+                ALOGW("command 0x%x generated error %d",
+                        command, cmdErr.error);
+            }
+        }
+    }
+
+    mWriter.reset();
+
+    return error;
+}
+
+CommandReader::~CommandReader()
+{
+    resetData();
+}
+
+Error CommandReader::parse()
+{
+    resetData();
+
+    IComposerClient::Command command;
+    uint16_t length = 0;
+
+    while (!isEmpty()) {
+        if (!beginCommand(&command, &length)) {
+            break;
+        }
+
+        bool parsed = false;
+        switch (command) {
+        case IComposerClient::Command::SELECT_DISPLAY:
+            parsed = parseSelectDisplay(length);
+            break;
+        case IComposerClient::Command::SET_ERROR:
+            parsed = parseSetError(length);
+            break;
+        case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+            parsed = parseSetChangedCompositionTypes(length);
+            break;
+        case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+            parsed = parseSetDisplayRequests(length);
+            break;
+        case IComposerClient::Command::SET_PRESENT_FENCE:
+            parsed = parseSetPresentFence(length);
+            break;
+        case IComposerClient::Command::SET_RELEASE_FENCES:
+            parsed = parseSetReleaseFences(length);
+            break;
+        case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
+            parsed = parseSetPresentOrValidateDisplayResult(length);
+            break;
+        default:
+            parsed = false;
+            break;
+        }
+
+        endCommand();
+
+        if (!parsed) {
+            ALOGE("failed to parse command 0x%x length %" PRIu16,
+                    command, length);
+            break;
+        }
+    }
+
+    return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
+}
+
+bool CommandReader::parseSelectDisplay(uint16_t length)
+{
+    if (length != CommandWriterBase::kSelectDisplayLength) {
+        return false;
+    }
+
+    mCurrentReturnData = &mReturnData[read64()];
+
+    return true;
+}
+
+bool CommandReader::parseSetError(uint16_t length)
+{
+    if (length != CommandWriterBase::kSetErrorLength) {
+        return false;
+    }
+
+    auto location = read();
+    auto error = static_cast<Error>(readSigned());
+
+    mErrors.emplace_back(CommandError{location, error});
+
+    return true;
+}
+
+bool CommandReader::parseSetChangedCompositionTypes(uint16_t length)
+{
+    // (layer id, composition type) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->changedLayers.reserve(count);
+    mCurrentReturnData->compositionTypes.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto type = static_cast<IComposerClient::Composition>(readSigned());
+
+        mCurrentReturnData->changedLayers.push_back(layer);
+        mCurrentReturnData->compositionTypes.push_back(type);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetDisplayRequests(uint16_t length)
+{
+    // display requests followed by (layer id, layer requests) pairs
+    if (length % 3 != 1 || !mCurrentReturnData) {
+        return false;
+    }
+
+    mCurrentReturnData->displayRequests = read();
+
+    uint32_t count = (length - 1) / 3;
+    mCurrentReturnData->requestedLayers.reserve(count);
+    mCurrentReturnData->requestMasks.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto layerRequestMask = read();
+
+        mCurrentReturnData->requestedLayers.push_back(layer);
+        mCurrentReturnData->requestMasks.push_back(layerRequestMask);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetPresentFence(uint16_t length)
+{
+    if (length != CommandWriterBase::kSetPresentFenceLength ||
+            !mCurrentReturnData) {
+        return false;
+    }
+
+    if (mCurrentReturnData->presentFence >= 0) {
+        close(mCurrentReturnData->presentFence);
+    }
+    mCurrentReturnData->presentFence = readFence();
+
+    return true;
+}
+
+bool CommandReader::parseSetReleaseFences(uint16_t length)
+{
+    // (layer id, release fence index) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->releasedLayers.reserve(count);
+    mCurrentReturnData->releaseFences.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto fence = readFence();
+
+        mCurrentReturnData->releasedLayers.push_back(layer);
+        mCurrentReturnData->releaseFences.push_back(fence);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length)
+{
+    if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->presentOrValidateState = read();
+    return true;
+}
+
+void CommandReader::resetData()
+{
+    mErrors.clear();
+
+    for (auto& data : mReturnData) {
+        if (data.second.presentFence >= 0) {
+            close(data.second.presentFence);
+        }
+        for (auto fence : data.second.releaseFences) {
+            if (fence >= 0) {
+                close(fence);
+            }
+        }
+    }
+
+    mReturnData.clear();
+    mCurrentReturnData = nullptr;
+}
+
+std::vector<CommandReader::CommandError> CommandReader::takeErrors()
+{
+    return std::move(mErrors);
+}
+
+bool CommandReader::hasChanges(Display display,
+        uint32_t* outNumChangedCompositionTypes,
+        uint32_t* outNumLayerRequestMasks) const
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outNumChangedCompositionTypes = 0;
+        *outNumLayerRequestMasks = 0;
+        return false;
+    }
+
+    const ReturnData& data = found->second;
+
+    *outNumChangedCompositionTypes = data.compositionTypes.size();
+    *outNumLayerRequestMasks = data.requestMasks.size();
+
+    return !(data.compositionTypes.empty() && data.requestMasks.empty());
+}
+
+void CommandReader::takeChangedCompositionTypes(Display display,
+        std::vector<Layer>* outLayers,
+        std::vector<IComposerClient::Composition>* outTypes)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outTypes->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.changedLayers);
+    *outTypes = std::move(data.compositionTypes);
+}
+
+void CommandReader::takeDisplayRequests(Display display,
+        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+        std::vector<uint32_t>* outLayerRequestMasks)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outDisplayRequestMask = 0;
+        outLayers->clear();
+        outLayerRequestMasks->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outDisplayRequestMask = data.displayRequests;
+    *outLayers = std::move(data.requestedLayers);
+    *outLayerRequestMasks = std::move(data.requestMasks);
+}
+
+void CommandReader::takeReleaseFences(Display display,
+        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outReleaseFences->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.releasedLayers);
+    *outReleaseFences = std::move(data.releaseFences);
+}
+
+void CommandReader::takePresentFence(Display display, int* outPresentFence)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outPresentFence = -1;
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outPresentFence = data.presentFence;
+    data.presentFence = -1;
+}
+
+void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *state= -1;
+        return;
+    }
+    ReturnData& data = found->second;
+    *state = data.presentOrValidateState;
+}
+
+} // namespace Hwc2
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
new file mode 100644
index 0000000..96dd833
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_COMPOSER_HAL_H
+#define ANDROID_SF_COMPOSER_HAL_H
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <utils/StrongPointer.h>
+#include <IComposerCommandBuffer.h>
+
+namespace android {
+
+namespace Hwc2 {
+
+using android::frameworks::vr::composer::V1_0::IVrComposerClient;
+
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Hdr;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_0::Transform;
+
+using android::hardware::graphics::composer::V2_1::IComposer;
+using android::hardware::graphics::composer::V2_1::IComposerCallback;
+using android::hardware::graphics::composer::V2_1::IComposerClient;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_1::Config;
+
+using android::hardware::graphics::composer::V2_1::CommandWriterBase;
+using android::hardware::graphics::composer::V2_1::CommandReaderBase;
+
+using android::hardware::kSynchronizedReadWrite;
+using android::hardware::MessageQueue;
+using android::hardware::MQDescriptorSync;
+using android::hardware::hidl_vec;
+using android::hardware::hidl_handle;
+
+class CommandReader : public CommandReaderBase {
+public:
+    ~CommandReader();
+
+    // Parse and execute commands from the command queue.  The commands are
+    // actually return values from the server and will be saved in ReturnData.
+    Error parse();
+
+    // Get and clear saved errors.
+    struct CommandError {
+        uint32_t location;
+        Error error;
+    };
+    std::vector<CommandError> takeErrors();
+
+    bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+            uint32_t* outNumLayerRequestMasks) const;
+
+    // Get and clear saved changed composition types.
+    void takeChangedCompositionTypes(Display display,
+            std::vector<Layer>* outLayers,
+            std::vector<IComposerClient::Composition>* outTypes);
+
+    // Get and clear saved display requests.
+    void takeDisplayRequests(Display display,
+        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+        std::vector<uint32_t>* outLayerRequestMasks);
+
+    // Get and clear saved release fences.
+    void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+            std::vector<int>* outReleaseFences);
+
+    // Get and clear saved present fence.
+    void takePresentFence(Display display, int* outPresentFence);
+
+    // Get what stage succeeded during PresentOrValidate: Present or Validate
+    void takePresentOrValidateStage(Display display, uint32_t * state);
+
+private:
+    void resetData();
+
+    bool parseSelectDisplay(uint16_t length);
+    bool parseSetError(uint16_t length);
+    bool parseSetChangedCompositionTypes(uint16_t length);
+    bool parseSetDisplayRequests(uint16_t length);
+    bool parseSetPresentFence(uint16_t length);
+    bool parseSetReleaseFences(uint16_t length);
+    bool parseSetPresentOrValidateDisplayResult(uint16_t length);
+
+    struct ReturnData {
+        uint32_t displayRequests = 0;
+
+        std::vector<Layer> changedLayers;
+        std::vector<IComposerClient::Composition> compositionTypes;
+
+        std::vector<Layer> requestedLayers;
+        std::vector<uint32_t> requestMasks;
+
+        int presentFence = -1;
+
+        std::vector<Layer> releasedLayers;
+        std::vector<int> releaseFences;
+
+        uint32_t presentOrValidateState;
+    };
+
+    std::vector<CommandError> mErrors;
+    std::unordered_map<Display, ReturnData> mReturnData;
+
+    // When SELECT_DISPLAY is parsed, this is updated to point to the
+    // display's return data in mReturnData.  We use it to avoid repeated
+    // map lookups.
+    ReturnData* mCurrentReturnData;
+};
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class Composer {
+public:
+    Composer(bool useVrComposer);
+
+    std::vector<IComposer::Capability> getCapabilities();
+    std::string dumpDebugInfo();
+
+    void registerCallback(const sp<IComposerCallback>& callback);
+
+    // Reset all pending commands in the command buffer. Useful if you want to
+    // skip a frame but have already queued some commands.
+    void resetCommands();
+
+    uint32_t getMaxVirtualDisplayCount();
+    bool isUsingVrComposer() const { return mIsUsingVrComposer; }
+    Error createVirtualDisplay(uint32_t width, uint32_t height,
+            PixelFormat* format, Display* outDisplay);
+    Error destroyVirtualDisplay(Display display);
+
+    Error acceptDisplayChanges(Display display);
+
+    Error createLayer(Display display, Layer* outLayer);
+    Error destroyLayer(Display display, Layer layer);
+
+    Error getActiveConfig(Display display, Config* outConfig);
+    Error getChangedCompositionTypes(Display display,
+            std::vector<Layer>* outLayers,
+            std::vector<IComposerClient::Composition>* outTypes);
+    Error getColorModes(Display display, std::vector<ColorMode>* outModes);
+    Error getDisplayAttribute(Display display, Config config,
+            IComposerClient::Attribute attribute, int32_t* outValue);
+    Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayName(Display display, std::string* outName);
+
+    Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+            std::vector<Layer>* outLayers,
+            std::vector<uint32_t>* outLayerRequestMasks);
+
+    Error getDisplayType(Display display,
+            IComposerClient::DisplayType* outType);
+    Error getDozeSupport(Display display, bool* outSupport);
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+            float* outMaxLuminance, float* outMaxAverageLuminance,
+            float* outMinLuminance);
+
+    Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+            std::vector<int>* outReleaseFences);
+
+    Error presentDisplay(Display display, int* outPresentFence);
+
+    Error setActiveConfig(Display display, Config config);
+
+    /*
+     * The composer caches client targets internally.  When target is nullptr,
+     * the composer uses slot to look up the client target from its cache.
+     * When target is not nullptr, the cache is updated with the new target.
+     */
+    Error setClientTarget(Display display, uint32_t slot,
+            const sp<GraphicBuffer>& target,
+            int acquireFence, Dataspace dataspace,
+            const std::vector<IComposerClient::Rect>& damage);
+    Error setColorMode(Display display, ColorMode mode);
+    Error setColorTransform(Display display, const float* matrix,
+            ColorTransform hint);
+    Error setOutputBuffer(Display display, const native_handle_t* buffer,
+            int releaseFence);
+    Error setPowerMode(Display display, IComposerClient::PowerMode mode);
+    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled);
+
+    Error setClientTargetSlotCount(Display display);
+
+    Error validateDisplay(Display display, uint32_t* outNumTypes,
+            uint32_t* outNumRequests);
+
+    Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                                   uint32_t* outNumRequests,
+                                   int* outPresentFence,
+                                   uint32_t* state);
+
+    Error setCursorPosition(Display display, Layer layer,
+            int32_t x, int32_t y);
+    /* see setClientTarget for the purpose of slot */
+    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+            const sp<GraphicBuffer>& buffer, int acquireFence);
+    Error setLayerSurfaceDamage(Display display, Layer layer,
+            const std::vector<IComposerClient::Rect>& damage);
+    Error setLayerBlendMode(Display display, Layer layer,
+            IComposerClient::BlendMode mode);
+    Error setLayerColor(Display display, Layer layer,
+            const IComposerClient::Color& color);
+    Error setLayerCompositionType(Display display, Layer layer,
+            IComposerClient::Composition type);
+    Error setLayerDataspace(Display display, Layer layer,
+            Dataspace dataspace);
+    Error setLayerDisplayFrame(Display display, Layer layer,
+            const IComposerClient::Rect& frame);
+    Error setLayerPlaneAlpha(Display display, Layer layer,
+            float alpha);
+    Error setLayerSidebandStream(Display display, Layer layer,
+            const native_handle_t* stream);
+    Error setLayerSourceCrop(Display display, Layer layer,
+            const IComposerClient::FRect& crop);
+    Error setLayerTransform(Display display, Layer layer,
+            Transform transform);
+    Error setLayerVisibleRegion(Display display, Layer layer,
+            const std::vector<IComposerClient::Rect>& visible);
+    Error setLayerZOrder(Display display, Layer layer, uint32_t z);
+    Error setLayerInfo(Display display, Layer layer, uint32_t type,
+                       uint32_t appId);
+private:
+    class CommandWriter : public CommandWriterBase {
+    public:
+        CommandWriter(uint32_t initialMaxSize);
+        ~CommandWriter() override;
+
+        void setLayerInfo(uint32_t type, uint32_t appId);
+        void setClientTargetMetadata(
+                const IVrComposerClient::BufferMetadata& metadata);
+        void setLayerBufferMetadata(
+                const IVrComposerClient::BufferMetadata& metadata);
+
+    private:
+        void writeBufferMetadata(
+                const IVrComposerClient::BufferMetadata& metadata);
+    };
+
+    // Many public functions above simply write a command into the command
+    // queue to batch the calls.  validateDisplay and presentDisplay will call
+    // this function to execute the command queue.
+    Error execute();
+
+    sp<IComposer> mComposer;
+    sp<IComposerClient> mClient;
+
+    // 64KiB minus a small space for metadata such as read/write pointers
+    static constexpr size_t kWriterInitialSize =
+        64 * 1024 / sizeof(uint32_t) - 16;
+    CommandWriter mWriter;
+    CommandReader mReader;
+
+    // When true, the we attach to the vr_hwcomposer service instead of the
+    // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr.
+    const bool mIsUsingVrComposer;
+};
+
+} // namespace Hwc2
+
+} // namespace android
+
+#endif // ANDROID_SF_COMPOSER_HAL_H
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
index d801bb3..cb08f08 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
@@ -25,6 +25,7 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+class Fence;
 class IGraphicBufferProducer;
 class String8;
 
diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h
deleted file mode 100644
index 9ad1040..0000000
--- a/services/surfaceflinger/DisplayHardware/FloatRect.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_FLOAT_RECT
-#define ANDROID_SF_FLOAT_RECT
-
-#include <ui/Rect.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-class FloatRect
-{
-public:
-    float left;
-    float top;
-    float right;
-    float bottom;
-
-    inline FloatRect()
-        : left(0), top(0), right(0), bottom(0) { }
-    inline FloatRect(const Rect& other)
-        : left(other.left), top(other.top), right(other.right), bottom(other.bottom) { }
-
-    inline float getWidth() const { return right - left; }
-    inline float getHeight() const { return bottom - top; }
-};
-
-}; // namespace android
-
-#endif // ANDROID_SF_FLOAT_RECT
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 7368d77..5b869e1 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -19,31 +19,27 @@
 #undef LOG_TAG
 #define LOG_TAG "FramebufferSurface"
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
 #include <errno.h>
-
-#include <cutils/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <utils/String8.h>
-
-#include <ui/Rect.h>
+#include <log/log.h>
 
 #include <EGL/egl.h>
 
 #include <hardware/hardware.h>
 #include <gui/BufferItem.h>
-#include <gui/GraphicBufferAlloc.h>
+#include <gui/BufferQueue.h>
 #include <gui/Surface.h>
+
 #include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
 
 #include "FramebufferSurface.h"
 #include "HWComposer.h"
-
-#ifndef NUM_FRAMEBUFFER_SURFACE_BUFFERS
-#define NUM_FRAMEBUFFER_SURFACE_BUFFERS (2)
-#endif
+#include "../SurfaceFlinger.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -88,7 +84,8 @@
     mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp));
     mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp));
 #endif
-    mConsumer->setMaxAcquiredBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS - 1);
+    mConsumer->setMaxAcquiredBufferCount(
+            SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
 status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
@@ -101,16 +98,18 @@
 
 status_t FramebufferSurface::advanceFrame() {
 #ifdef USE_HWC2
+    uint32_t slot = 0;
     sp<GraphicBuffer> buf;
     sp<Fence> acquireFence(Fence::NO_FENCE);
     android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
-    status_t result = nextBuffer(buf, acquireFence, dataspace);
+    status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
                 strerror(-result), result);
         return result;
     }
-    result = mHwc.setClientTarget(mDisplayType, acquireFence, buf, dataspace);
+    result = mHwc.setClientTarget(mDisplayType, slot,
+            acquireFence, buf, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error posting framebuffer: %d", result);
     }
@@ -124,8 +123,9 @@
 }
 
 #ifdef USE_HWC2
-status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer,
-        sp<Fence>& outFence, android_dataspace_t& outDataspace) {
+status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
+        sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
+        android_dataspace_t& outDataspace) {
 #else
 status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
 #endif
@@ -134,7 +134,12 @@
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+#ifdef USE_HWC2
+        mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+                &outSlot, &outBuffer);
+#else
         outBuffer = mCurrentBuffer;
+#endif
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -170,9 +175,12 @@
     mCurrentFence = item.mFence;
 
     outFence = item.mFence;
-    outBuffer = mCurrentBuffer;
 #ifdef USE_HWC2
+    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+            &outSlot, &outBuffer);
     outDataspace = item.mDataSpace;
+#else
+    outBuffer = mCurrentBuffer;
 #endif
     return NO_ERROR;
 }
@@ -205,7 +213,7 @@
 void FramebufferSurface::onFrameCommitted() {
 #ifdef USE_HWC2
     if (mHasPendingRelease) {
-        sp<Fence> fence = mHwc.getRetireFence(mDisplayType);
+        sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
         if (fence->isValid()) {
             status_t result = addReleaseFence(mPreviousBufferSlot,
                     mPreviousBuffer, fence);
@@ -240,7 +248,7 @@
 #endif
 
 void FramebufferSurface::dumpAsString(String8& result) const {
-    ConsumerBase::dump(result);
+    ConsumerBase::dumpState(result);
 }
 
 void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 439435a..69a72d7 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,13 +17,14 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
+#include "DisplaySurface.h"
+#include "HWComposerBufferCache.h"
+
 #include <stdint.h>
 #include <sys/types.h>
 
 #include <gui/ConsumerBase.h>
 
-#include "DisplaySurface.h"
-
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -68,8 +69,8 @@
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
 #ifdef USE_HWC2
-    status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
-            android_dataspace_t& outDataspace);
+    status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
+            sp<Fence>& outFence, android_dataspace_t& outDataspace);
 #else
     status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
 #endif
@@ -93,6 +94,8 @@
     HWComposer& mHwc;
 
 #ifdef USE_HWC2
+    HWComposerBufferCache mHwcBufferCache;
+
     // Previous buffer to release after getting an updated retire fence
     bool mHasPendingRelease;
     int mPreviousBufferSlot;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4fe3cfd..c48a28f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -21,10 +21,10 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "HWC2.h"
-
-#include "FloatRect.h"
+#include "ComposerHal.h"
 
 #include <ui/Fence.h>
+#include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Region.h>
 
@@ -79,55 +79,17 @@
 using android::Rect;
 using android::Region;
 using android::sp;
+using android::hardware::Return;
+using android::hardware::Void;
 
 namespace HWC2 {
 
+namespace Hwc2 = android::Hwc2;
+
 // Device methods
 
-Device::Device(hwc2_device_t* device)
-  : mHwcDevice(device),
-    mCreateVirtualDisplay(nullptr),
-    mDestroyVirtualDisplay(nullptr),
-    mDump(nullptr),
-    mGetMaxVirtualDisplayCount(nullptr),
-    mRegisterCallback(nullptr),
-    mAcceptDisplayChanges(nullptr),
-    mCreateLayer(nullptr),
-    mDestroyLayer(nullptr),
-    mGetActiveConfig(nullptr),
-    mGetChangedCompositionTypes(nullptr),
-    mGetColorModes(nullptr),
-    mGetDisplayAttribute(nullptr),
-    mGetDisplayConfigs(nullptr),
-    mGetDisplayName(nullptr),
-    mGetDisplayRequests(nullptr),
-    mGetDisplayType(nullptr),
-    mGetDozeSupport(nullptr),
-    mGetHdrCapabilities(nullptr),
-    mGetReleaseFences(nullptr),
-    mPresentDisplay(nullptr),
-    mSetActiveConfig(nullptr),
-    mSetClientTarget(nullptr),
-    mSetColorMode(nullptr),
-    mSetColorTransform(nullptr),
-    mSetOutputBuffer(nullptr),
-    mSetPowerMode(nullptr),
-    mSetVsyncEnabled(nullptr),
-    mValidateDisplay(nullptr),
-    mSetCursorPosition(nullptr),
-    mSetLayerBuffer(nullptr),
-    mSetLayerSurfaceDamage(nullptr),
-    mSetLayerBlendMode(nullptr),
-    mSetLayerColor(nullptr),
-    mSetLayerCompositionType(nullptr),
-    mSetLayerDataspace(nullptr),
-    mSetLayerDisplayFrame(nullptr),
-    mSetLayerPlaneAlpha(nullptr),
-    mSetLayerSidebandStream(nullptr),
-    mSetLayerSourceCrop(nullptr),
-    mSetLayerTransform(nullptr),
-    mSetLayerVisibleRegion(nullptr),
-    mSetLayerZOrder(nullptr),
+Device::Device(bool useVrComposer)
+  : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
     mCapabilities(),
     mDisplays(),
     mHotplug(),
@@ -138,16 +100,11 @@
     mPendingVsyncs()
 {
     loadCapabilities();
-    loadFunctionPointers();
     registerCallbacks();
 }
 
 Device::~Device()
 {
-    if (mHwcDevice == nullptr) {
-        return;
-    }
-
     for (auto element : mDisplays) {
         auto display = element.second.lock();
         if (!display) {
@@ -174,26 +131,18 @@
             }
         }
     }
-
-    hwc2_close(mHwcDevice);
 }
 
 // Required by HWC2 device
 
 std::string Device::dump() const
 {
-    uint32_t numBytes = 0;
-    mDump(mHwcDevice, &numBytes, nullptr);
-
-    std::vector<char> buffer(numBytes);
-    mDump(mHwcDevice, &numBytes, buffer.data());
-
-    return std::string(buffer.data(), buffer.size());
+    return mComposer->dumpDebugInfo();
 }
 
 uint32_t Device::getMaxVirtualDisplayCount() const
 {
-    return mGetMaxVirtualDisplayCount(mHwcDevice);
+    return mComposer->getMaxVirtualDisplayCount();
 }
 
 Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
@@ -202,8 +151,8 @@
     ALOGI("Creating virtual display");
 
     hwc2_display_t displayId = 0;
-    int32_t intFormat = static_cast<int32_t>(*format);
-    int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
+    auto intFormat = static_cast<Hwc2::PixelFormat>(*format);
+    auto intError = mComposer->createVirtualDisplay(width, height,
             &intFormat, &displayId);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
@@ -217,7 +166,7 @@
         ALOGE("Failed to get display by id");
         return Error::BadDisplay;
     }
-    (*outDisplay)->setVirtual();
+    (*outDisplay)->setConnected(true);
     return Error::None;
 }
 
@@ -258,6 +207,7 @@
 {
     if (connected == Connection::Connected) {
         if (!display->isConnected()) {
+            mComposer->setClientTargetSlotCount(display->getId());
             display->loadConfigs();
             display->setConnected(true);
         }
@@ -315,13 +265,9 @@
 {
     static_assert(sizeof(Capability) == sizeof(int32_t),
             "Capability size has changed");
-    uint32_t numCapabilities = 0;
-    mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
-    std::vector<Capability> capabilities(numCapabilities);
-    auto asInt = reinterpret_cast<int32_t*>(capabilities.data());
-    mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
+    auto capabilities = mComposer->getCapabilities();
     for (auto capability : capabilities) {
-        mCapabilities.emplace(capability);
+        mCapabilities.emplace(static_cast<Capability>(capability));
     }
 }
 
@@ -331,108 +277,39 @@
             capability) != mCapabilities.cend();
 }
 
-void Device::loadFunctionPointers()
-{
-    // For all of these early returns, we log an error message inside
-    // loadFunctionPointer specifying which function failed to load
+namespace {
+class ComposerCallback : public Hwc2::IComposerCallback {
+public:
+    ComposerCallback(Device* device) : mDevice(device) {}
 
-    // Display function pointers
-    if (!loadFunctionPointer(FunctionDescriptor::CreateVirtualDisplay,
-            mCreateVirtualDisplay)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::DestroyVirtualDisplay,
-            mDestroyVirtualDisplay)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::Dump, mDump)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetMaxVirtualDisplayCount,
-            mGetMaxVirtualDisplayCount)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::RegisterCallback,
-            mRegisterCallback)) return;
-
-    // Device function pointers
-    if (!loadFunctionPointer(FunctionDescriptor::AcceptDisplayChanges,
-            mAcceptDisplayChanges)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::CreateLayer,
-            mCreateLayer)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::DestroyLayer,
-            mDestroyLayer)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetActiveConfig,
-            mGetActiveConfig)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes,
-            mGetChangedCompositionTypes)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetColorModes,
-            mGetColorModes)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute,
-            mGetDisplayAttribute)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs,
-            mGetDisplayConfigs)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayName,
-            mGetDisplayName)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayRequests,
-            mGetDisplayRequests)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayType,
-            mGetDisplayType)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetDozeSupport,
-            mGetDozeSupport)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetHdrCapabilities,
-            mGetHdrCapabilities)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::GetReleaseFences,
-            mGetReleaseFences)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::PresentDisplay,
-            mPresentDisplay)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetActiveConfig,
-            mSetActiveConfig)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetClientTarget,
-            mSetClientTarget)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetColorMode,
-            mSetColorMode)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetColorTransform,
-            mSetColorTransform)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer,
-            mSetOutputBuffer)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetPowerMode,
-            mSetPowerMode)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetVsyncEnabled,
-            mSetVsyncEnabled)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::ValidateDisplay,
-            mValidateDisplay)) return;
-
-    // Layer function pointers
-    if (!loadFunctionPointer(FunctionDescriptor::SetCursorPosition,
-            mSetCursorPosition)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerBuffer,
-            mSetLayerBuffer)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerSurfaceDamage,
-            mSetLayerSurfaceDamage)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerBlendMode,
-            mSetLayerBlendMode)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerColor,
-            mSetLayerColor)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType,
-            mSetLayerCompositionType)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerDataspace,
-            mSetLayerDataspace)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame,
-            mSetLayerDisplayFrame)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
-            mSetLayerPlaneAlpha)) return;
-    if (hasCapability(Capability::SidebandStream)) {
-        if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
-                mSetLayerSidebandStream)) return;
+    Return<void> onHotplug(Hwc2::Display display,
+            Connection connected) override
+    {
+        hotplug_hook(mDevice, display, static_cast<int32_t>(connected));
+        return Void();
     }
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
-            mSetLayerSourceCrop)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
-            mSetLayerTransform)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerVisibleRegion,
-            mSetLayerVisibleRegion)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
-            mSetLayerZOrder)) return;
-}
+
+    Return<void> onRefresh(Hwc2::Display display) override
+    {
+        refresh_hook(mDevice, display);
+        return Void();
+    }
+
+    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
+    {
+        vsync_hook(mDevice, display, timestamp);
+        return Void();
+    }
+
+private:
+    Device* mDevice;
+};
+} // namespace anonymous
 
 void Device::registerCallbacks()
 {
-    registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
-    registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
-    registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+    sp<ComposerCallback> callback = new ComposerCallback(this);
+    mComposer->registerCallback(callback);
 }
 
 
@@ -441,7 +318,7 @@
 void Device::destroyVirtualDisplay(hwc2_display_t display)
 {
     ALOGI("Destroying virtual display");
-    int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+    auto intError = mComposer->destroyVirtualDisplay(display);
     auto error = static_cast<Error>(intError);
     ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
             " %s (%d)", display, to_string(error).c_str(), intError);
@@ -454,15 +331,23 @@
   : mDevice(device),
     mId(id),
     mIsConnected(false),
-    mIsVirtual(false)
+    mType(DisplayType::Invalid)
 {
     ALOGV("Created display %" PRIu64, id);
+
+    auto intError = mDevice.mComposer->getDisplayType(mId,
+            reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(&mType));
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d)",
+              id, to_string(error).c_str(), intError);
+    }
 }
 
 Display::~Display()
 {
     ALOGV("Destroyed display %" PRIu64, mId);
-    if (mIsVirtual) {
+    if (mType == DisplayType::Virtual) {
         mDevice.destroyVirtualDisplay(mId);
     }
 }
@@ -498,14 +383,14 @@
 
 Error Display::acceptChanges()
 {
-    int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId);
+    auto intError = mDevice.mComposer->acceptDisplayChanges(mId);
     return static_cast<Error>(intError);
 }
 
 Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
 {
     hwc2_layer_t layerId = 0;
-    int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
+    auto intError = mDevice.mComposer->createLayer(mId, &layerId);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -522,11 +407,12 @@
 {
     ALOGV("[%" PRIu64 "] getActiveConfig", mId);
     hwc2_config_t configId = 0;
-    int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId,
-            &configId);
+    auto intError = mDevice.mComposer->getActiveConfig(mId, &configId);
     auto error = static_cast<Error>(intError);
 
     if (error != Error::None) {
+        ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
+        *outConfig = nullptr;
         return error;
     }
 
@@ -546,18 +432,12 @@
 Error Display::getChangedCompositionTypes(
         std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes)
 {
-    uint32_t numElements = 0;
-    int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice,
-            mId, &numElements, nullptr, nullptr);
+    std::vector<Hwc2::Layer> layerIds;
+    std::vector<Hwc2::IComposerClient::Composition> types;
+    auto intError = mDevice.mComposer->getChangedCompositionTypes(mId,
+            &layerIds, &types);
+    uint32_t numElements = layerIds.size();
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    std::vector<hwc2_layer_t> layerIds(numElements);
-    std::vector<int32_t> types(numElements);
-    intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId,
-            &numElements, layerIds.data(), types.data());
     error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -583,18 +463,10 @@
 
 Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const
 {
-    uint32_t numModes = 0;
-    int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
-            &numModes, nullptr);
+    std::vector<Hwc2::ColorMode> modes;
+    auto intError = mDevice.mComposer->getColorModes(mId, &modes);
+    uint32_t numModes = modes.size();
     auto error = static_cast<Error>(intError);
-    if (error != Error::None)  {
-        return error;
-    }
-
-    std::vector<int32_t> modes(numModes);
-    intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, &numModes,
-            modes.data());
-    error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
     }
@@ -617,49 +489,25 @@
 
 Error Display::getName(std::string* outName) const
 {
-    uint32_t size;
-    int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
-            nullptr);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    std::vector<char> rawName(size);
-    intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
-            rawName.data());
-    error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    *outName = std::string(rawName.cbegin(), rawName.cend());
-    return Error::None;
+    auto intError = mDevice.mComposer->getDisplayName(mId, outName);
+    return static_cast<Error>(intError);
 }
 
 Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
         std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
                 outLayerRequests)
 {
-    int32_t intDisplayRequests = 0;
-    uint32_t numElements = 0;
-    int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
-            &intDisplayRequests, &numElements, nullptr, nullptr);
+    uint32_t intDisplayRequests;
+    std::vector<Hwc2::Layer> layerIds;
+    std::vector<uint32_t> layerRequests;
+    auto intError = mDevice.mComposer->getDisplayRequests(mId,
+            &intDisplayRequests, &layerIds, &layerRequests);
+    uint32_t numElements = layerIds.size();
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
     }
 
-    std::vector<hwc2_layer_t> layerIds(numElements);
-    std::vector<int32_t> layerRequests(numElements);
-    intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
-            &intDisplayRequests, &numElements, layerIds.data(),
-            layerRequests.data());
-    error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
     *outDisplayRequests = static_cast<DisplayRequest>(intDisplayRequests);
     outLayerRequests->clear();
     outLayerRequests->reserve(numElements);
@@ -680,23 +528,14 @@
 
 Error Display::getType(DisplayType* outType) const
 {
-    int32_t intType = 0;
-    int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
-            &intType);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    *outType = static_cast<DisplayType>(intType);
+    *outType = mType;
     return Error::None;
 }
 
 Error Display::supportsDoze(bool* outSupport) const
 {
-    int32_t intSupport = 0;
-    int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId,
-            &intSupport);
+    bool intSupport = false;
+    auto intError = mDevice.mComposer->getDozeSupport(mId, &intSupport);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -712,18 +551,16 @@
     float maxLuminance = -1.0f;
     float maxAverageLuminance = -1.0f;
     float minLuminance = -1.0f;
-    int32_t intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId,
-            &numTypes, nullptr, &maxLuminance, &maxAverageLuminance,
-            &minLuminance);
+    std::vector<Hwc2::Hdr> intTypes;
+    auto intError = mDevice.mComposer->getHdrCapabilities(mId, &intTypes,
+            &maxLuminance, &maxAverageLuminance, &minLuminance);
     auto error = static_cast<HWC2::Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
 
-    std::vector<int32_t> types(numTypes);
-    intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId, &numTypes,
-            types.data(), &maxLuminance, &maxAverageLuminance, &minLuminance);
-    error = static_cast<HWC2::Error>(intError);
+    std::vector<int32_t> types;
+    for (auto type : intTypes) {
+        types.push_back(static_cast<int32_t>(type));
+    }
+    numTypes = types.size();
     if (error != Error::None) {
         return error;
     }
@@ -736,19 +573,12 @@
 Error Display::getReleaseFences(
         std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
 {
-    uint32_t numElements = 0;
-    int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId,
-            &numElements, nullptr, nullptr);
+    std::vector<Hwc2::Layer> layerIds;
+    std::vector<int> fenceFds;
+    auto intError = mDevice.mComposer->getReleaseFences(mId,
+            &layerIds, &fenceFds);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    std::vector<hwc2_layer_t> layerIds(numElements);
-    std::vector<int32_t> fenceFds(numElements);
-    intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements,
-            layerIds.data(), fenceFds.data());
-    error = static_cast<Error>(intError);
+    uint32_t numElements = layerIds.size();
     if (error != Error::None) {
         return error;
     }
@@ -763,6 +593,9 @@
         } else {
             ALOGE("getReleaseFences: invalid layer %" PRIu64
                     " found on display %" PRIu64, layerIds[element], mId);
+            for (; element < numElements; ++element) {
+                close(fenceFds[element]);
+            }
             return Error::BadLayer;
         }
     }
@@ -771,17 +604,16 @@
     return Error::None;
 }
 
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
 {
-    int32_t retireFenceFd = 0;
-    int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
-            &retireFenceFd);
+    int32_t presentFenceFd = -1;
+    auto intError = mDevice.mComposer->presentDisplay(mId, &presentFenceFd);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
     }
 
-    *outRetireFence = new Fence(retireFenceFd);
+    *outPresentFence = new Fence(presentFenceFd);
     return Error::None;
 }
 
@@ -793,32 +625,33 @@
                 config->getDisplayId(), mId);
         return Error::BadConfig;
     }
-    int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
-            config->getId());
+    auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId());
     return static_cast<Error>(intError);
 }
 
-Error Display::setClientTarget(buffer_handle_t target,
+Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target,
         const sp<Fence>& acquireFence, android_dataspace_t dataspace)
 {
     // TODO: Properly encode client target surface damage
     int32_t fenceFd = acquireFence->dup();
-    int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
-            fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+    auto intError = mDevice.mComposer->setClientTarget(mId, slot, target,
+            fenceFd, static_cast<Hwc2::Dataspace>(dataspace),
+            std::vector<Hwc2::IComposerClient::Rect>());
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorMode(android_color_mode_t mode)
 {
-    int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+    auto intError = mDevice.mComposer->setColorMode(mId,
+            static_cast<Hwc2::ColorMode>(mode));
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorTransform(const android::mat4& matrix,
         android_color_transform_t hint)
 {
-    int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
-            matrix.asArray(), static_cast<int32_t>(hint));
+    auto intError = mDevice.mComposer->setColorTransform(mId,
+            matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
     return static_cast<Error>(intError);
 }
 
@@ -827,24 +660,22 @@
 {
     int32_t fenceFd = releaseFence->dup();
     auto handle = buffer->getNativeBuffer()->handle;
-    int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
-            fenceFd);
+    auto intError = mDevice.mComposer->setOutputBuffer(mId, handle, fenceFd);
     close(fenceFd);
     return static_cast<Error>(intError);
 }
 
 Error Display::setPowerMode(PowerMode mode)
 {
-    auto intMode = static_cast<int32_t>(mode);
-    int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode);
+    auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
+    auto intError = mDevice.mComposer->setPowerMode(mId, intMode);
     return static_cast<Error>(intError);
 }
 
 Error Display::setVsyncEnabled(Vsync enabled)
 {
-    auto intEnabled = static_cast<int32_t>(enabled);
-    int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId,
-            intEnabled);
+    auto intEnabled = static_cast<Hwc2::IComposerClient::Vsync>(enabled);
+    auto intError = mDevice.mComposer->setVsyncEnabled(mId, intEnabled);
     return static_cast<Error>(intError);
 }
 
@@ -852,7 +683,7 @@
 {
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
-    int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId,
+    auto intError = mDevice.mComposer->validateDisplay(mId,
             &numTypes, &numRequests);
     auto error = static_cast<Error>(intError);
     if (error != Error::None && error != Error::HasChanges) {
@@ -864,13 +695,42 @@
     return error;
 }
 
+Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                 sp<android::Fence>* outPresentFence, uint32_t* state) {
+
+    uint32_t numTypes = 0;
+    uint32_t numRequests = 0;
+    int32_t presentFenceFd = -1;
+    auto intError = mDevice.mComposer->presentOrValidateDisplay(mId, &numTypes, &numRequests, &presentFenceFd, state);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None && error != Error::HasChanges) {
+        return error;
+    }
+
+    if (*state == 1) {
+        *outPresentFence = new Fence(presentFenceFd);
+    }
+
+    if (*state == 0) {
+        *outNumTypes = numTypes;
+        *outNumRequests = numRequests;
+    }
+    return error;
+}
+
+void Display::discardCommands()
+{
+    mDevice.mComposer->resetCommands();
+}
+
 // For use by Device
 
 int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
 {
     int32_t value = 0;
-    int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId,
-            configId, static_cast<int32_t>(attribute), &value);
+    auto intError = mDevice.mComposer->getDisplayAttribute(mId, configId,
+            static_cast<Hwc2::IComposerClient::Attribute>(attribute),
+            &value);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
@@ -899,21 +759,10 @@
 {
     ALOGV("[%" PRIu64 "] loadConfigs", mId);
 
-    uint32_t numConfigs = 0;
-    int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
-            &numConfigs, nullptr);
+    std::vector<Hwc2::Config> configIds;
+    auto intError = mDevice.mComposer->getDisplayConfigs(mId, &configIds);
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
-        ALOGE("[%" PRIu64 "] getDisplayConfigs [1] failed: %s (%d)", mId,
-                to_string(error).c_str(), intError);
-        return;
-    }
-
-    std::vector<hwc2_config_t> configIds(numConfigs);
-    intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs,
-            configIds.data());
-    error = static_cast<Error>(intError);
-    if (error != Error::None) {
         ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
                 to_string(error).c_str(), intError);
         return;
@@ -928,7 +777,7 @@
 
 void Display::destroyLayer(hwc2_layer_t layerId)
 {
-    int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId);
+    auto intError =mDevice.mComposer->destroyLayer(mId, layerId);
     auto error = static_cast<Error>(intError);
     ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
             " failed: %s (%d)", mId, layerId, to_string(error).c_str(),
@@ -970,17 +819,17 @@
 
 Error Layer::setCursorPosition(int32_t x, int32_t y)
 {
-    int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
-            mDisplayId, mId, x, y);
+    auto intError = mDevice.mComposer->setCursorPosition(mDisplayId,
+            mId, x, y);
     return static_cast<Error>(intError);
 }
 
-Error Layer::setBuffer(buffer_handle_t buffer,
+Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer,
         const sp<Fence>& acquireFence)
 {
     int32_t fenceFd = acquireFence->dup();
-    int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
-            mId, buffer, fenceFd);
+    auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+            mId, slot, buffer, fenceFd);
     return static_cast<Error>(intError);
 }
 
@@ -988,26 +837,22 @@
 {
     // We encode default full-screen damage as INVALID_RECT upstream, but as 0
     // rects for HWC
-    int32_t intError = 0;
+    Hwc2::Error intError = Hwc2::Error::NONE;
     if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
-        intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
-                mDisplayId, mId, {0, nullptr});
+        intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+                mId, std::vector<Hwc2::IComposerClient::Rect>());
     } else {
         size_t rectCount = 0;
         auto rectArray = damage.getArray(&rectCount);
 
-        std::vector<hwc_rect_t> hwcRects;
+        std::vector<Hwc2::IComposerClient::Rect> hwcRects;
         for (size_t rect = 0; rect < rectCount; ++rect) {
             hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
                     rectArray[rect].right, rectArray[rect].bottom});
         }
 
-        hwc_region_t hwcRegion = {};
-        hwcRegion.numRects = rectCount;
-        hwcRegion.rects = hwcRects.data();
-
-        intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
-                mDisplayId, mId, hwcRegion);
+        intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+                mId, hwcRects);
     }
 
     return static_cast<Error>(intError);
@@ -1015,47 +860,49 @@
 
 Error Layer::setBlendMode(BlendMode mode)
 {
-    auto intMode = static_cast<int32_t>(mode);
-    int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice,
-            mDisplayId, mId, intMode);
+    auto intMode = static_cast<Hwc2::IComposerClient::BlendMode>(mode);
+    auto intError = mDevice.mComposer->setLayerBlendMode(mDisplayId,
+            mId, intMode);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setColor(hwc_color_t color)
 {
-    int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId,
-            mId, color);
+    Hwc2::IComposerClient::Color hwcColor{color.r, color.g, color.b, color.a};
+    auto intError = mDevice.mComposer->setLayerColor(mDisplayId,
+            mId, hwcColor);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setCompositionType(Composition type)
 {
-    auto intType = static_cast<int32_t>(type);
-    int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
-            mDisplayId, mId, intType);
+    auto intType = static_cast<Hwc2::IComposerClient::Composition>(type);
+    auto intError = mDevice.mComposer->setLayerCompositionType(mDisplayId,
+            mId, intType);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setDataspace(android_dataspace_t dataspace)
 {
-    auto intDataspace = static_cast<int32_t>(dataspace);
-    int32_t intError = mDevice.mSetLayerDataspace(mDevice.mHwcDevice,
-            mDisplayId, mId, intDataspace);
+    auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace);
+    auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId,
+            mId, intDataspace);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setDisplayFrame(const Rect& frame)
 {
-    hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
-    int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice,
-            mDisplayId, mId, hwcRect);
+    Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top,
+        frame.right, frame.bottom};
+    auto intError = mDevice.mComposer->setLayerDisplayFrame(mDisplayId,
+            mId, hwcRect);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setPlaneAlpha(float alpha)
 {
-    int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice,
-            mDisplayId, mId, alpha);
+    auto intError = mDevice.mComposer->setLayerPlaneAlpha(mDisplayId,
+            mId, alpha);
     return static_cast<Error>(intError);
 }
 
@@ -1066,24 +913,25 @@
                 "device supports sideband streams");
         return Error::Unsupported;
     }
-    int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
-            mDisplayId, mId, stream);
+    auto intError = mDevice.mComposer->setLayerSidebandStream(mDisplayId,
+            mId, stream);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setSourceCrop(const FloatRect& crop)
 {
-    hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom};
-    int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice,
-            mDisplayId, mId, hwcRect);
+    Hwc2::IComposerClient::FRect hwcRect{
+        crop.left, crop.top, crop.right, crop.bottom};
+    auto intError = mDevice.mComposer->setLayerSourceCrop(mDisplayId,
+            mId, hwcRect);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setTransform(Transform transform)
 {
-    auto intTransform = static_cast<int32_t>(transform);
-    int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice,
-            mDisplayId, mId, intTransform);
+    auto intTransform = static_cast<Hwc2::Transform>(transform);
+    auto intError = mDevice.mComposer->setLayerTransform(mDisplayId,
+            mId, intTransform);
     return static_cast<Error>(intError);
 }
 
@@ -1092,26 +940,27 @@
     size_t rectCount = 0;
     auto rectArray = region.getArray(&rectCount);
 
-    std::vector<hwc_rect_t> hwcRects;
+    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
     for (size_t rect = 0; rect < rectCount; ++rect) {
         hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
                 rectArray[rect].right, rectArray[rect].bottom});
     }
 
-    hwc_region_t hwcRegion = {};
-    hwcRegion.numRects = rectCount;
-    hwcRegion.rects = hwcRects.data();
-
-    int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice,
-            mDisplayId, mId, hwcRegion);
+    auto intError = mDevice.mComposer->setLayerVisibleRegion(mDisplayId,
+            mId, hwcRects);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setZOrder(uint32_t z)
 {
-    int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId,
-            mId, z);
+    auto intError = mDevice.mComposer->setLayerZOrder(mDisplayId, mId, z);
     return static_cast<Error>(intError);
 }
 
+Error Layer::setInfo(uint32_t type, uint32_t appId)
+{
+  auto intError = mDevice.mComposer->setLayerInfo(mDisplayId, mId, type, appId);
+  return static_cast<Error>(intError);
+}
+
 } // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index fb04af8..7463e3b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -24,7 +24,7 @@
 #undef HWC2_USE_CPP11
 
 #include <ui/HdrCapabilities.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
@@ -35,6 +35,7 @@
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
+#include <map>
 
 namespace android {
     class Fence;
@@ -42,6 +43,9 @@
     class GraphicBuffer;
     class Rect;
     class Region;
+    namespace Hwc2 {
+        class Composer;
+    }
 }
 
 namespace HWC2 {
@@ -54,10 +58,15 @@
 typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback;
 typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback;
 
+// C++ Wrapper around hwc2_device_t. Load all functions pointers
+// and handle callback registration.
 class Device
 {
 public:
-    Device(hwc2_device_t* device);
+    // useVrComposer is passed to the composer HAL. When true, the composer HAL
+    // will use the vr composer service, otherwise it uses the real hardware
+    // composer.
+    Device(bool useVrComposer);
     ~Device();
 
     friend class HWC2::Display;
@@ -95,35 +104,12 @@
 
     bool hasCapability(HWC2::Capability capability) const;
 
+    android::Hwc2::Composer* getComposer() { return mComposer.get(); }
+
 private:
     // Initialization methods
 
-    template <typename PFN>
-    [[clang::warn_unused_result]] bool loadFunctionPointer(
-            FunctionDescriptor desc, PFN& outPFN) {
-        auto intDesc = static_cast<int32_t>(desc);
-        auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);
-        if (pfn != nullptr) {
-            outPFN = reinterpret_cast<PFN>(pfn);
-            return true;
-        } else {
-            ALOGE("Failed to load function %s", to_string(desc).c_str());
-            return false;
-        }
-    }
-
-    template <typename PFN, typename HOOK>
-    void registerCallback(Callback callback, HOOK hook) {
-        static_assert(std::is_same<PFN, HOOK>::value,
-                "Incompatible function pointer");
-        auto intCallback = static_cast<int32_t>(callback);
-        auto callbackData = static_cast<hwc2_callback_data_t>(this);
-        auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
-        mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
-    }
-
     void loadCapabilities();
-    void loadFunctionPointers();
     void registerCallbacks();
 
     // For use by Display
@@ -131,56 +117,7 @@
     void destroyVirtualDisplay(hwc2_display_t display);
 
     // Member variables
-
-    hwc2_device_t* mHwcDevice;
-
-    // Device function pointers
-    HWC2_PFN_CREATE_VIRTUAL_DISPLAY mCreateVirtualDisplay;
-    HWC2_PFN_DESTROY_VIRTUAL_DISPLAY mDestroyVirtualDisplay;
-    HWC2_PFN_DUMP mDump;
-    HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT mGetMaxVirtualDisplayCount;
-    HWC2_PFN_REGISTER_CALLBACK mRegisterCallback;
-
-    // Display function pointers
-    HWC2_PFN_ACCEPT_DISPLAY_CHANGES mAcceptDisplayChanges;
-    HWC2_PFN_CREATE_LAYER mCreateLayer;
-    HWC2_PFN_DESTROY_LAYER mDestroyLayer;
-    HWC2_PFN_GET_ACTIVE_CONFIG mGetActiveConfig;
-    HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES mGetChangedCompositionTypes;
-    HWC2_PFN_GET_COLOR_MODES mGetColorModes;
-    HWC2_PFN_GET_DISPLAY_ATTRIBUTE mGetDisplayAttribute;
-    HWC2_PFN_GET_DISPLAY_CONFIGS mGetDisplayConfigs;
-    HWC2_PFN_GET_DISPLAY_NAME mGetDisplayName;
-    HWC2_PFN_GET_DISPLAY_REQUESTS mGetDisplayRequests;
-    HWC2_PFN_GET_DISPLAY_TYPE mGetDisplayType;
-    HWC2_PFN_GET_DOZE_SUPPORT mGetDozeSupport;
-    HWC2_PFN_GET_HDR_CAPABILITIES mGetHdrCapabilities;
-    HWC2_PFN_GET_RELEASE_FENCES mGetReleaseFences;
-    HWC2_PFN_PRESENT_DISPLAY mPresentDisplay;
-    HWC2_PFN_SET_ACTIVE_CONFIG mSetActiveConfig;
-    HWC2_PFN_SET_CLIENT_TARGET mSetClientTarget;
-    HWC2_PFN_SET_COLOR_MODE mSetColorMode;
-    HWC2_PFN_SET_COLOR_TRANSFORM mSetColorTransform;
-    HWC2_PFN_SET_OUTPUT_BUFFER mSetOutputBuffer;
-    HWC2_PFN_SET_POWER_MODE mSetPowerMode;
-    HWC2_PFN_SET_VSYNC_ENABLED mSetVsyncEnabled;
-    HWC2_PFN_VALIDATE_DISPLAY mValidateDisplay;
-
-    // Layer function pointers
-    HWC2_PFN_SET_CURSOR_POSITION mSetCursorPosition;
-    HWC2_PFN_SET_LAYER_BUFFER mSetLayerBuffer;
-    HWC2_PFN_SET_LAYER_SURFACE_DAMAGE mSetLayerSurfaceDamage;
-    HWC2_PFN_SET_LAYER_BLEND_MODE mSetLayerBlendMode;
-    HWC2_PFN_SET_LAYER_COLOR mSetLayerColor;
-    HWC2_PFN_SET_LAYER_COMPOSITION_TYPE mSetLayerCompositionType;
-    HWC2_PFN_SET_LAYER_DATASPACE mSetLayerDataspace;
-    HWC2_PFN_SET_LAYER_DISPLAY_FRAME mSetLayerDisplayFrame;
-    HWC2_PFN_SET_LAYER_PLANE_ALPHA mSetLayerPlaneAlpha;
-    HWC2_PFN_SET_LAYER_SIDEBAND_STREAM mSetLayerSidebandStream;
-    HWC2_PFN_SET_LAYER_SOURCE_CROP mSetLayerSourceCrop;
-    HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
-    HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
-    HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+    std::unique_ptr<android::Hwc2::Composer> mComposer;
 
     std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
@@ -194,6 +131,7 @@
     std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs;
 };
 
+// Convenience C++ class to access hwc2_device_t Display functions directly.
 class Display : public std::enable_shared_from_this<Display>
 {
 public:
@@ -300,11 +238,11 @@
             std::unordered_map<std::shared_ptr<Layer>,
                     android::sp<android::Fence>>* outFences) const;
     [[clang::warn_unused_result]] Error present(
-            android::sp<android::Fence>* outRetireFence);
+            android::sp<android::Fence>* outPresentFence);
     [[clang::warn_unused_result]] Error setActiveConfig(
             const std::shared_ptr<const Config>& config);
     [[clang::warn_unused_result]] Error setClientTarget(
-            buffer_handle_t target,
+            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
             const android::sp<android::Fence>& acquireFence,
             android_dataspace_t dataspace);
     [[clang::warn_unused_result]] Error setColorMode(android_color_mode_t mode);
@@ -317,6 +255,15 @@
     [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
     [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
             uint32_t* outNumRequests);
+    [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
+                                                 uint32_t* outNumRequests,
+                                                          android::sp<android::Fence>* outPresentFence, uint32_t* state);
+
+    // Most methods in this class write a command to a command buffer.  The
+    // command buffer is implicitly submitted in validate, present, and
+    // presentOrValidate.  This method provides a way to discard the commands,
+    // which can be used to discard stale commands.
+    void discardCommands();
 
     // Other Display methods
 
@@ -327,12 +274,6 @@
 private:
     // For use by Device
 
-    // Virtual displays are always connected
-    void setVirtual() {
-        mIsVirtual = true;
-        mIsConnected = true;
-    }
-
     void setConnected(bool connected) { mIsConnected = connected; }
     int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
     void loadConfig(hwc2_config_t configId);
@@ -350,11 +291,14 @@
     Device& mDevice;
     hwc2_display_t mId;
     bool mIsConnected;
-    bool mIsVirtual;
+    DisplayType mType;
     std::unordered_map<hwc2_layer_t, std::weak_ptr<Layer>> mLayers;
-    std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+    // The ordering in this map matters, for getConfigs(), when it is
+    // converted to a vector
+    std::map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
 };
 
+// Convenience C++ class to access hwc2_device_t Layer functions directly.
 class Layer
 {
 public:
@@ -365,7 +309,8 @@
     hwc2_layer_t getId() const { return mId; }
 
     [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y);
-    [[clang::warn_unused_result]] Error setBuffer(buffer_handle_t buffer,
+    [[clang::warn_unused_result]] Error setBuffer(uint32_t slot,
+            const android::sp<android::GraphicBuffer>& buffer,
             const android::sp<android::Fence>& acquireFence);
     [[clang::warn_unused_result]] Error setSurfaceDamage(
             const android::Region& damage);
@@ -386,6 +331,7 @@
     [[clang::warn_unused_result]] Error setVisibleRegion(
             const android::Region& region);
     [[clang::warn_unused_result]] Error setZOrder(uint32_t z);
+    [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId);
 
 private:
     std::weak_ptr<Display> mDisplay;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
deleted file mode 100644
index cc0dfb0..0000000
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ /dev/null
@@ -1,2666 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-
-#undef LOG_TAG
-#define LOG_TAG "HWC2On1Adapter"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "HWC2On1Adapter.h"
-
-#include <hardware/hwcomposer.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-#include <cstdlib>
-#include <chrono>
-#include <inttypes.h>
-#include <sstream>
-
-using namespace std::chrono_literals;
-
-static bool operator==(const hwc_color_t& lhs, const hwc_color_t& rhs) {
-    return lhs.r == rhs.r &&
-            lhs.g == rhs.g &&
-            lhs.b == rhs.b &&
-            lhs.a == rhs.a;
-}
-
-static bool operator==(const hwc_rect_t& lhs, const hwc_rect_t& rhs) {
-    return lhs.left == rhs.left &&
-            lhs.top == rhs.top &&
-            lhs.right == rhs.right &&
-            lhs.bottom == rhs.bottom;
-}
-
-static bool operator==(const hwc_frect_t& lhs, const hwc_frect_t& rhs) {
-    return lhs.left == rhs.left &&
-            lhs.top == rhs.top &&
-            lhs.right == rhs.right &&
-            lhs.bottom == rhs.bottom;
-}
-
-template <typename T>
-static inline bool operator!=(const T& lhs, const T& rhs)
-{
-    return !(lhs == rhs);
-}
-
-static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
-{
-    auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
-    return (version >> 16) & 0xF;
-}
-
-template <typename PFN, typename T>
-static hwc2_function_pointer_t asFP(T function)
-{
-    static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
-    return reinterpret_cast<hwc2_function_pointer_t>(function);
-}
-
-using namespace HWC2;
-
-static constexpr Attribute ColorMode = static_cast<Attribute>(6);
-
-namespace android {
-
-void HWC2On1Adapter::DisplayContentsDeleter::operator()(
-        hwc_display_contents_1_t* contents)
-{
-    if (contents != nullptr) {
-        for (size_t l = 0; l < contents->numHwLayers; ++l) {
-            auto& layer = contents->hwLayers[l];
-            std::free(const_cast<hwc_rect_t*>(layer.visibleRegionScreen.rects));
-        }
-    }
-    std::free(contents);
-}
-
-class HWC2On1Adapter::Callbacks : public hwc_procs_t {
-    public:
-        Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
-            invalidate = &invalidateHook;
-            vsync = &vsyncHook;
-            hotplug = &hotplugHook;
-        }
-
-        static void invalidateHook(const hwc_procs_t* procs) {
-            auto callbacks = static_cast<const Callbacks*>(procs);
-            callbacks->mAdapter.hwc1Invalidate();
-        }
-
-        static void vsyncHook(const hwc_procs_t* procs, int display,
-                int64_t timestamp) {
-            auto callbacks = static_cast<const Callbacks*>(procs);
-            callbacks->mAdapter.hwc1Vsync(display, timestamp);
-        }
-
-        static void hotplugHook(const hwc_procs_t* procs, int display,
-                int connected) {
-            auto callbacks = static_cast<const Callbacks*>(procs);
-            callbacks->mAdapter.hwc1Hotplug(display, connected);
-        }
-
-    private:
-        HWC2On1Adapter& mAdapter;
-};
-
-static int closeHook(hw_device_t* /*device*/)
-{
-    // Do nothing, since the real work is done in the class destructor, but we
-    // need to provide a valid function pointer for hwc2_close to call
-    return 0;
-}
-
-HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
-  : mDumpString(),
-    mHwc1Device(hwc1Device),
-    mHwc1MinorVersion(getMinorVersion(hwc1Device)),
-    mHwc1SupportsVirtualDisplays(false),
-    mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
-    mCapabilities(),
-    mLayers(),
-    mHwc1VirtualDisplay(),
-    mStateMutex(),
-    mCallbacks(),
-    mHasPendingInvalidate(false),
-    mPendingVsyncs(),
-    mPendingHotplugs(),
-    mDisplays(),
-    mHwc1DisplayMap()
-{
-    common.close = closeHook;
-    getCapabilities = getCapabilitiesHook;
-    getFunction = getFunctionHook;
-    populateCapabilities();
-    populatePrimary();
-    mHwc1Device->registerProcs(mHwc1Device,
-            static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
-}
-
-HWC2On1Adapter::~HWC2On1Adapter() {
-    hwc_close_1(mHwc1Device);
-}
-
-void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
-        int32_t* outCapabilities)
-{
-    if (outCapabilities == nullptr) {
-        *outCount = mCapabilities.size();
-        return;
-    }
-
-    auto capabilityIter = mCapabilities.cbegin();
-    for (size_t written = 0; written < *outCount; ++written) {
-        if (capabilityIter == mCapabilities.cend()) {
-            return;
-        }
-        outCapabilities[written] = static_cast<int32_t>(*capabilityIter);
-        ++capabilityIter;
-    }
-}
-
-hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
-        FunctionDescriptor descriptor)
-{
-    switch (descriptor) {
-        // Device functions
-        case FunctionDescriptor::CreateVirtualDisplay:
-            return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
-                    createVirtualDisplayHook);
-        case FunctionDescriptor::DestroyVirtualDisplay:
-            return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
-                    destroyVirtualDisplayHook);
-        case FunctionDescriptor::Dump:
-            return asFP<HWC2_PFN_DUMP>(dumpHook);
-        case FunctionDescriptor::GetMaxVirtualDisplayCount:
-            return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
-                    getMaxVirtualDisplayCountHook);
-        case FunctionDescriptor::RegisterCallback:
-            return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
-
-        // Display functions
-        case FunctionDescriptor::AcceptDisplayChanges:
-            return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
-                    displayHook<decltype(&Display::acceptChanges),
-                    &Display::acceptChanges>);
-        case FunctionDescriptor::CreateLayer:
-            return asFP<HWC2_PFN_CREATE_LAYER>(
-                    displayHook<decltype(&Display::createLayer),
-                    &Display::createLayer, hwc2_layer_t*>);
-        case FunctionDescriptor::DestroyLayer:
-            return asFP<HWC2_PFN_DESTROY_LAYER>(
-                    displayHook<decltype(&Display::destroyLayer),
-                    &Display::destroyLayer, hwc2_layer_t>);
-        case FunctionDescriptor::GetActiveConfig:
-            return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(
-                    displayHook<decltype(&Display::getActiveConfig),
-                    &Display::getActiveConfig, hwc2_config_t*>);
-        case FunctionDescriptor::GetChangedCompositionTypes:
-            return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
-                    displayHook<decltype(&Display::getChangedCompositionTypes),
-                    &Display::getChangedCompositionTypes, uint32_t*,
-                    hwc2_layer_t*, int32_t*>);
-        case FunctionDescriptor::GetColorModes:
-            return asFP<HWC2_PFN_GET_COLOR_MODES>(
-                    displayHook<decltype(&Display::getColorModes),
-                    &Display::getColorModes, uint32_t*, int32_t*>);
-        case FunctionDescriptor::GetDisplayAttribute:
-            return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
-                    getDisplayAttributeHook);
-        case FunctionDescriptor::GetDisplayConfigs:
-            return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(
-                    displayHook<decltype(&Display::getConfigs),
-                    &Display::getConfigs, uint32_t*, hwc2_config_t*>);
-        case FunctionDescriptor::GetDisplayName:
-            return asFP<HWC2_PFN_GET_DISPLAY_NAME>(
-                    displayHook<decltype(&Display::getName),
-                    &Display::getName, uint32_t*, char*>);
-        case FunctionDescriptor::GetDisplayRequests:
-            return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(
-                    displayHook<decltype(&Display::getRequests),
-                    &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*,
-                    int32_t*>);
-        case FunctionDescriptor::GetDisplayType:
-            return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(
-                    displayHook<decltype(&Display::getType),
-                    &Display::getType, int32_t*>);
-        case FunctionDescriptor::GetDozeSupport:
-            return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
-                    displayHook<decltype(&Display::getDozeSupport),
-                    &Display::getDozeSupport, int32_t*>);
-        case FunctionDescriptor::GetHdrCapabilities:
-            return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(
-                    displayHook<decltype(&Display::getHdrCapabilities),
-                    &Display::getHdrCapabilities, uint32_t*, int32_t*, float*,
-                    float*, float*>);
-        case FunctionDescriptor::GetReleaseFences:
-            return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
-                    displayHook<decltype(&Display::getReleaseFences),
-                    &Display::getReleaseFences, uint32_t*, hwc2_layer_t*,
-                    int32_t*>);
-        case FunctionDescriptor::PresentDisplay:
-            return asFP<HWC2_PFN_PRESENT_DISPLAY>(
-                    displayHook<decltype(&Display::present),
-                    &Display::present, int32_t*>);
-        case FunctionDescriptor::SetActiveConfig:
-            return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(
-                    displayHook<decltype(&Display::setActiveConfig),
-                    &Display::setActiveConfig, hwc2_config_t>);
-        case FunctionDescriptor::SetClientTarget:
-            return asFP<HWC2_PFN_SET_CLIENT_TARGET>(
-                    displayHook<decltype(&Display::setClientTarget),
-                    &Display::setClientTarget, buffer_handle_t, int32_t,
-                    int32_t, hwc_region_t>);
-        case FunctionDescriptor::SetColorMode:
-            return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
-        case FunctionDescriptor::SetColorTransform:
-            return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
-        case FunctionDescriptor::SetOutputBuffer:
-            return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
-                    displayHook<decltype(&Display::setOutputBuffer),
-                    &Display::setOutputBuffer, buffer_handle_t, int32_t>);
-        case FunctionDescriptor::SetPowerMode:
-            return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
-        case FunctionDescriptor::SetVsyncEnabled:
-            return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
-        case FunctionDescriptor::ValidateDisplay:
-            return asFP<HWC2_PFN_VALIDATE_DISPLAY>(
-                    displayHook<decltype(&Display::validate),
-                    &Display::validate, uint32_t*, uint32_t*>);
-
-        // Layer functions
-        case FunctionDescriptor::SetCursorPosition:
-            return asFP<HWC2_PFN_SET_CURSOR_POSITION>(
-                    layerHook<decltype(&Layer::setCursorPosition),
-                    &Layer::setCursorPosition, int32_t, int32_t>);
-        case FunctionDescriptor::SetLayerBuffer:
-            return asFP<HWC2_PFN_SET_LAYER_BUFFER>(
-                    layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer,
-                    buffer_handle_t, int32_t>);
-        case FunctionDescriptor::SetLayerSurfaceDamage:
-            return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
-                    layerHook<decltype(&Layer::setSurfaceDamage),
-                    &Layer::setSurfaceDamage, hwc_region_t>);
-
-        // Layer state functions
-        case FunctionDescriptor::SetLayerBlendMode:
-            return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(
-                    setLayerBlendModeHook);
-        case FunctionDescriptor::SetLayerColor:
-            return asFP<HWC2_PFN_SET_LAYER_COLOR>(
-                    layerHook<decltype(&Layer::setColor), &Layer::setColor,
-                    hwc_color_t>);
-        case FunctionDescriptor::SetLayerCompositionType:
-            return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
-                    setLayerCompositionTypeHook);
-        case FunctionDescriptor::SetLayerDataspace:
-            return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook);
-        case FunctionDescriptor::SetLayerDisplayFrame:
-            return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
-                    layerHook<decltype(&Layer::setDisplayFrame),
-                    &Layer::setDisplayFrame, hwc_rect_t>);
-        case FunctionDescriptor::SetLayerPlaneAlpha:
-            return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
-                    layerHook<decltype(&Layer::setPlaneAlpha),
-                    &Layer::setPlaneAlpha, float>);
-        case FunctionDescriptor::SetLayerSidebandStream:
-            return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
-                    layerHook<decltype(&Layer::setSidebandStream),
-                    &Layer::setSidebandStream, const native_handle_t*>);
-        case FunctionDescriptor::SetLayerSourceCrop:
-            return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
-                    layerHook<decltype(&Layer::setSourceCrop),
-                    &Layer::setSourceCrop, hwc_frect_t>);
-        case FunctionDescriptor::SetLayerTransform:
-            return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook);
-        case FunctionDescriptor::SetLayerVisibleRegion:
-            return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
-                    layerHook<decltype(&Layer::setVisibleRegion),
-                    &Layer::setVisibleRegion, hwc_region_t>);
-        case FunctionDescriptor::SetLayerZOrder:
-            return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook);
-
-        default:
-            ALOGE("doGetFunction: Unknown function descriptor: %d (%s)",
-                    static_cast<int32_t>(descriptor),
-                    to_string(descriptor).c_str());
-            return nullptr;
-    }
-}
-
-// Device functions
-
-Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
-        uint32_t height, hwc2_display_t* outDisplay)
-{
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    if (mHwc1VirtualDisplay) {
-        // We have already allocated our only HWC1 virtual display
-        ALOGE("createVirtualDisplay: HWC1 virtual display already allocated");
-        return Error::NoResources;
-    }
-
-    if (MAX_VIRTUAL_DISPLAY_DIMENSION != 0 &&
-            (width > MAX_VIRTUAL_DISPLAY_DIMENSION ||
-            height > MAX_VIRTUAL_DISPLAY_DIMENSION)) {
-        ALOGE("createVirtualDisplay: Can't create a virtual display with"
-                " a dimension > %u (tried %u x %u)",
-                MAX_VIRTUAL_DISPLAY_DIMENSION, width, height);
-        return Error::NoResources;
-    }
-
-    mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this,
-            HWC2::DisplayType::Virtual);
-    mHwc1VirtualDisplay->populateConfigs(width, height);
-    const auto displayId = mHwc1VirtualDisplay->getId();
-    mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId;
-    mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL);
-    mDisplays.emplace(displayId, mHwc1VirtualDisplay);
-    *outDisplay = displayId;
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId)
-{
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
-        return Error::BadDisplay;
-    }
-
-    mHwc1VirtualDisplay.reset();
-    mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL);
-    mDisplays.erase(displayId);
-
-    return Error::None;
-}
-
-void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer)
-{
-    if (outBuffer != nullptr) {
-        auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
-        *outSize = static_cast<uint32_t>(copiedBytes);
-        return;
-    }
-
-    std::stringstream output;
-
-    output << "-- HWC2On1Adapter --\n";
-
-    output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) <<
-            " device\n";
-
-    // Attempt to acquire the lock for 1 second, but proceed without the lock
-    // after that, so we can still get some information if we're deadlocked
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex,
-            std::defer_lock);
-    lock.try_lock_for(1s);
-
-    if (mCapabilities.empty()) {
-        output << "Capabilities: None\n";
-    } else {
-        output << "Capabilities:\n";
-        for (auto capability : mCapabilities) {
-            output << "  " << to_string(capability) << '\n';
-        }
-    }
-
-    output << "Displays:\n";
-    for (const auto& element : mDisplays) {
-        const auto& display = element.second;
-        output << display->dump();
-    }
-    output << '\n';
-
-    // Release the lock before calling into HWC1, and since we no longer require
-    // mutual exclusion to access mCapabilities or mDisplays
-    lock.unlock();
-
-    if (mHwc1Device->dump) {
-        output << "HWC1 dump:\n";
-        std::vector<char> hwc1Dump(4096);
-        // Call with size - 1 to preserve a null character at the end
-        mHwc1Device->dump(mHwc1Device, hwc1Dump.data(),
-                static_cast<int>(hwc1Dump.size() - 1));
-        output << hwc1Dump.data();
-    }
-
-    mDumpString = output.str();
-    *outSize = static_cast<uint32_t>(mDumpString.size());
-}
-
-uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount()
-{
-    return mHwc1SupportsVirtualDisplays ? 1 : 0;
-}
-
-static bool isValid(Callback descriptor) {
-    switch (descriptor) {
-        case Callback::Hotplug: // Fall-through
-        case Callback::Refresh: // Fall-through
-        case Callback::Vsync: return true;
-        default: return false;
-    }
-}
-
-Error HWC2On1Adapter::registerCallback(Callback descriptor,
-        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer)
-{
-    if (!isValid(descriptor)) {
-        return Error::BadParameter;
-    }
-
-    ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(),
-            callbackData, pointer);
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    mCallbacks[descriptor] = {callbackData, pointer};
-
-    bool hasPendingInvalidate = false;
-    std::vector<hwc2_display_t> displayIds;
-    std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
-    std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;
-
-    if (descriptor == Callback::Refresh) {
-        hasPendingInvalidate = mHasPendingInvalidate;
-        if (hasPendingInvalidate) {
-            for (auto& displayPair : mDisplays) {
-                displayIds.emplace_back(displayPair.first);
-            }
-        }
-        mHasPendingInvalidate = false;
-    } else if (descriptor == Callback::Vsync) {
-        for (auto pending : mPendingVsyncs) {
-            auto hwc1DisplayId = pending.first;
-            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
-                ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
-                        hwc1DisplayId);
-                continue;
-            }
-            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
-            auto timestamp = pending.second;
-            pendingVsyncs.emplace_back(displayId, timestamp);
-        }
-        mPendingVsyncs.clear();
-    } else if (descriptor == Callback::Hotplug) {
-        // Hotplug the primary display
-        pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
-                static_cast<int32_t>(Connection::Connected));
-
-        for (auto pending : mPendingHotplugs) {
-            auto hwc1DisplayId = pending.first;
-            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
-                ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
-                        hwc1DisplayId);
-                continue;
-            }
-            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
-            auto connected = pending.second;
-            pendingHotplugs.emplace_back(displayId, connected);
-        }
-    }
-
-    // Call pending callbacks without the state lock held
-    lock.unlock();
-
-    if (hasPendingInvalidate) {
-        auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
-        for (auto displayId : displayIds) {
-            refresh(callbackData, displayId);
-        }
-    }
-    if (!pendingVsyncs.empty()) {
-        auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
-        for (auto& pendingVsync : pendingVsyncs) {
-            vsync(callbackData, pendingVsync.first, pendingVsync.second);
-        }
-    }
-    if (!pendingHotplugs.empty()) {
-        auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
-        for (auto& pendingHotplug : pendingHotplugs) {
-            hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
-        }
-    }
-    return Error::None;
-}
-
-// Display functions
-
-std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1);
-
-HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
-  : mId(sNextId++),
-    mDevice(device),
-    mDirtyCount(0),
-    mStateMutex(),
-    mZIsDirty(false),
-    mHwc1RequestedContents(nullptr),
-    mHwc1ReceivedContents(nullptr),
-    mRetireFence(),
-    mChanges(),
-    mHwc1Id(-1),
-    mConfigs(),
-    mActiveConfig(nullptr),
-    mName(),
-    mType(type),
-    mPowerMode(PowerMode::Off),
-    mVsyncEnabled(Vsync::Invalid),
-    mClientTarget(),
-    mOutputBuffer(),
-    mHasColorTransform(false),
-    mLayers(),
-    mHwc1LayerMap() {}
-
-Error HWC2On1Adapter::Display::acceptChanges()
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!mChanges) {
-        ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId);
-        return Error::NotValidated;
-    }
-
-    ALOGV("[%" PRIu64 "] acceptChanges", mId);
-
-    for (auto& change : mChanges->getTypeChanges()) {
-        auto layerId = change.first;
-        auto type = change.second;
-        auto layer = mDevice.mLayers[layerId];
-        layer->setCompositionType(type);
-    }
-
-    mChanges->clearTypeChanges();
-
-    mHwc1RequestedContents = std::move(mHwc1ReceivedContents);
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
-    mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
-    *outLayerId = layer->getId();
-    ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    const auto mapLayer = mDevice.mLayers.find(layerId);
-    if (mapLayer == mDevice.mLayers.end()) {
-        ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer",
-                mId, layerId);
-        return Error::BadLayer;
-    }
-    const auto layer = mapLayer->second;
-    mDevice.mLayers.erase(mapLayer);
-    const auto zRange = mLayers.equal_range(layer);
-    for (auto current = zRange.first; current != zRange.second; ++current) {
-        if (**current == *layer) {
-            current = mLayers.erase(current);
-            break;
-        }
-    }
-    ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!mActiveConfig) {
-        ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId,
-                to_string(Error::BadConfig).c_str());
-        return Error::BadConfig;
-    }
-    auto configId = mActiveConfig->getId();
-    ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId);
-    *outConfig = configId;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
-        Attribute attribute, int32_t* outValue)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
-        ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId,
-                configId);
-        return Error::BadConfig;
-    }
-    *outValue = mConfigs[configId]->getAttribute(attribute);
-    ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId,
-            to_string(attribute).c_str(), *outValue);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getChangedCompositionTypes(
-        uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!mChanges) {
-        ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated",
-                mId);
-        return Error::NotValidated;
-    }
-
-    if ((outLayers == nullptr) || (outTypes == nullptr)) {
-        *outNumElements = mChanges->getTypeChanges().size();
-        return Error::None;
-    }
-
-    uint32_t numWritten = 0;
-    for (const auto& element : mChanges->getTypeChanges()) {
-        if (numWritten == *outNumElements) {
-            break;
-        }
-        auto layerId = element.first;
-        auto intType = static_cast<int32_t>(element.second);
-        ALOGV("Adding %" PRIu64 " %s", layerId,
-                to_string(element.second).c_str());
-        outLayers[numWritten] = layerId;
-        outTypes[numWritten] = intType;
-        ++numWritten;
-    }
-    *outNumElements = numWritten;
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
-        int32_t* outModes)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!outModes) {
-        *outNumModes = mColorModes.size();
-        return Error::None;
-    }
-    uint32_t numModes = std::min(*outNumModes,
-            static_cast<uint32_t>(mColorModes.size()));
-    std::copy_n(mColorModes.cbegin(), numModes, outModes);
-    *outNumModes = numModes;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
-        hwc2_config_t* outConfigs)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!outConfigs) {
-        *outNumConfigs = mConfigs.size();
-        return Error::None;
-    }
-    uint32_t numWritten = 0;
-    for (const auto& config : mConfigs) {
-        if (numWritten == *outNumConfigs) {
-            break;
-        }
-        outConfigs[numWritten] = config->getId();
-        ++numWritten;
-    }
-    *outNumConfigs = numWritten;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
-        *outSupport = 0;
-    } else {
-        *outSupport = 1;
-    }
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
-        int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
-        float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/)
-{
-    // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
-    *outNumTypes = 0;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!outName) {
-        *outSize = mName.size();
-        return Error::None;
-    }
-    auto numCopied = mName.copy(outName, *outSize);
-    *outSize = numCopied;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
-        hwc2_layer_t* outLayers, int32_t* outFences)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    uint32_t numWritten = 0;
-    bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr);
-    for (const auto& layer : mLayers) {
-        if (outputsNonNull && (numWritten == *outNumElements)) {
-            break;
-        }
-
-        auto releaseFence = layer->getReleaseFence();
-        if (releaseFence != Fence::NO_FENCE) {
-            if (outputsNonNull) {
-                outLayers[numWritten] = layer->getId();
-                outFences[numWritten] = releaseFence->dup();
-            }
-            ++numWritten;
-        }
-    }
-    *outNumElements = numWritten;
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
-        uint32_t* outNumElements, hwc2_layer_t* outLayers,
-        int32_t* outLayerRequests)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!mChanges) {
-        return Error::NotValidated;
-    }
-
-    if (outLayers == nullptr || outLayerRequests == nullptr) {
-        *outNumElements = mChanges->getNumLayerRequests();
-        return Error::None;
-    }
-
-    *outDisplayRequests = mChanges->getDisplayRequests();
-    uint32_t numWritten = 0;
-    for (const auto& request : mChanges->getLayerRequests()) {
-        if (numWritten == *outNumElements) {
-            break;
-        }
-        outLayers[numWritten] = request.first;
-        outLayerRequests[numWritten] = static_cast<int32_t>(request.second);
-        ++numWritten;
-    }
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::getType(int32_t* outType)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    *outType = static_cast<int32_t>(mType);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::present(int32_t* outRetireFence)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (mChanges) {
-        Error error = mDevice.setAllDisplays();
-        if (error != Error::None) {
-            ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId,
-                    to_string(error).c_str());
-            return error;
-        }
-    }
-
-    *outRetireFence = mRetireFence.get()->dup();
-    ALOGV("[%" PRIu64 "] present returning retire fence %d", mId,
-            *outRetireFence);
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    auto config = getConfig(configId);
-    if (!config) {
-        return Error::BadConfig;
-    }
-    if (config == mActiveConfig) {
-        return Error::None;
-    }
-
-    if (mDevice.mHwc1MinorVersion >= 4) {
-        uint32_t hwc1Id = 0;
-        auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id);
-        if (error != Error::None) {
-            return error;
-        }
-
-        int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
-                mHwc1Id, static_cast<int>(hwc1Id));
-        if (intError != 0) {
-            ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)",
-                intError);
-            return Error::BadConfig;
-        }
-        mActiveConfig = config;
-    }
-
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
-        int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
-    mClientTarget.setBuffer(target);
-    mClientTarget.setFence(acquireFence);
-    // dataspace and damage can't be used by HWC1, so ignore them
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode)
-{
-    std::unique_lock<std::recursive_mutex> lock (mStateMutex);
-
-    ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
-
-    if (mode == mActiveColorMode) {
-        return Error::None;
-    }
-    if (mColorModes.count(mode) == 0) {
-        ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode);
-        return Error::Unsupported;
-    }
-
-    uint32_t hwc1Config = 0;
-    auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config);
-    if (error != Error::None) {
-        return error;
-    }
-
-    ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config);
-    int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
-            mHwc1Id, hwc1Config);
-    if (intError != 0) {
-        ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError);
-        return Error::Unsupported;
-    }
-
-    mActiveColorMode = mode;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
-            static_cast<int32_t>(hint));
-    mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
-        int32_t releaseFence)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
-    mOutputBuffer.setBuffer(buffer);
-    mOutputBuffer.setFence(releaseFence);
-    return Error::None;
-}
-
-static bool isValid(PowerMode mode)
-{
-    switch (mode) {
-        case PowerMode::Off: // Fall-through
-        case PowerMode::DozeSuspend: // Fall-through
-        case PowerMode::Doze: // Fall-through
-        case PowerMode::On: return true;
-        default: return false;
-    }
-}
-
-static int getHwc1PowerMode(PowerMode mode)
-{
-    switch (mode) {
-        case PowerMode::Off: return HWC_POWER_MODE_OFF;
-        case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
-        case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
-        case PowerMode::On: return HWC_POWER_MODE_NORMAL;
-        default: return HWC_POWER_MODE_OFF;
-    }
-}
-
-Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode)
-{
-    if (!isValid(mode)) {
-        return Error::BadParameter;
-    }
-    if (mode == mPowerMode) {
-        return Error::None;
-    }
-
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    int error = 0;
-    if (mDevice.mHwc1MinorVersion < 4) {
-        error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id,
-                mode == PowerMode::Off);
-    } else {
-        error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device,
-                mHwc1Id, getHwc1PowerMode(mode));
-    }
-    ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)",
-            error);
-
-    ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str());
-    mPowerMode = mode;
-    return Error::None;
-}
-
-static bool isValid(Vsync enable) {
-    switch (enable) {
-        case Vsync::Enable: // Fall-through
-        case Vsync::Disable: return true;
-        default: return false;
-    }
-}
-
-Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable)
-{
-    if (!isValid(enable)) {
-        return Error::BadParameter;
-    }
-    if (enable == mVsyncEnabled) {
-        return Error::None;
-    }
-
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device,
-            mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable);
-    ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)",
-            error);
-
-    mVsyncEnabled = enable;
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
-        uint32_t* outNumRequests)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    ALOGV("[%" PRIu64 "] Entering validate", mId);
-
-    if (!mChanges) {
-        if (!mDevice.prepareAllDisplays()) {
-            return Error::BadDisplay;
-        }
-    }
-
-    *outNumTypes = mChanges->getNumTypes();
-    *outNumRequests = mChanges->getNumLayerRequests();
-    ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes,
-            *outNumRequests);
-    for (auto request : mChanges->getTypeChanges()) {
-        ALOGV("Layer %" PRIu64 " --> %s", request.first,
-                to_string(request.second).c_str());
-    }
-    return *outNumTypes > 0 ? Error::HasChanges : Error::None;
-}
-
-// Display helpers
-
-Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    const auto mapLayer = mDevice.mLayers.find(layerId);
-    if (mapLayer == mDevice.mLayers.end()) {
-        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId);
-        return Error::BadLayer;
-    }
-
-    const auto layer = mapLayer->second;
-    const auto zRange = mLayers.equal_range(layer);
-    bool layerOnDisplay = false;
-    for (auto current = zRange.first; current != zRange.second; ++current) {
-        if (**current == *layer) {
-            if ((*current)->getZ() == z) {
-                // Don't change anything if the Z hasn't changed
-                return Error::None;
-            }
-            current = mLayers.erase(current);
-            layerOnDisplay = true;
-            break;
-        }
-    }
-
-    if (!layerOnDisplay) {
-        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display",
-                mId);
-        return Error::BadLayer;
-    }
-
-    layer->setZ(z);
-    mLayers.emplace(std::move(layer));
-    mZIsDirty = true;
-
-    return Error::None;
-}
-
-static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = {
-    HWC_DISPLAY_VSYNC_PERIOD,
-    HWC_DISPLAY_WIDTH,
-    HWC_DISPLAY_HEIGHT,
-    HWC_DISPLAY_DPI_X,
-    HWC_DISPLAY_DPI_Y,
-    HWC_DISPLAY_COLOR_TRANSFORM,
-    HWC_DISPLAY_NO_ATTRIBUTE,
-};
-
-static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = {
-    HWC_DISPLAY_VSYNC_PERIOD,
-    HWC_DISPLAY_WIDTH,
-    HWC_DISPLAY_HEIGHT,
-    HWC_DISPLAY_DPI_X,
-    HWC_DISPLAY_DPI_Y,
-    HWC_DISPLAY_NO_ATTRIBUTE,
-};
-
-static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR =
-        sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t);
-static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR),
-        "Attribute tables have unexpected sizes");
-
-static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = {
-    6, // HWC_DISPLAY_NO_ATTRIBUTE = 0
-    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
-    1, // HWC_DISPLAY_WIDTH = 2,
-    2, // HWC_DISPLAY_HEIGHT = 3,
-    3, // HWC_DISPLAY_DPI_X = 4,
-    4, // HWC_DISPLAY_DPI_Y = 5,
-    5, // HWC_DISPLAY_COLOR_TRANSFORM = 6,
-};
-
-static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = {
-    5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
-    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
-    1, // HWC_DISPLAY_WIDTH = 2,
-    2, // HWC_DISPLAY_HEIGHT = 3,
-    3, // HWC_DISPLAY_DPI_X = 4,
-    4, // HWC_DISPLAY_DPI_Y = 5,
-};
-
-template <uint32_t attribute>
-static constexpr bool attributesMatch()
-{
-    bool match = (attribute ==
-            ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]);
-    if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) {
-        return match;
-    }
-
-    return match && (attribute ==
-            ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]);
-}
-static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
-        "Tables out of sync");
-static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync");
-static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
-static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
-static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
-static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
-        "Tables out of sync");
-
-void HWC2On1Adapter::Display::populateConfigs()
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    ALOGV("[%" PRIu64 "] populateConfigs", mId);
-
-    if (mHwc1Id == -1) {
-        ALOGE("populateConfigs: HWC1 ID not set");
-        return;
-    }
-
-    const size_t MAX_NUM_CONFIGS = 128;
-    uint32_t configs[MAX_NUM_CONFIGS] = {};
-    size_t numConfigs = MAX_NUM_CONFIGS;
-    mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id,
-            configs, &numConfigs);
-
-    for (size_t c = 0; c < numConfigs; ++c) {
-        uint32_t hwc1ConfigId = configs[c];
-        auto newConfig = std::make_shared<Config>(*this);
-
-        int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {};
-        bool hasColor = true;
-        auto result = mDevice.mHwc1Device->getDisplayAttributes(
-                mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId,
-                ATTRIBUTES_WITH_COLOR, values);
-        if (result != 0) {
-            mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device,
-                    mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values);
-            hasColor = false;
-        }
-
-        auto attributeMap = hasColor ?
-                ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR;
-
-        newConfig->setAttribute(Attribute::VsyncPeriod,
-                values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]);
-        newConfig->setAttribute(Attribute::Width,
-                values[attributeMap[HWC_DISPLAY_WIDTH]]);
-        newConfig->setAttribute(Attribute::Height,
-                values[attributeMap[HWC_DISPLAY_HEIGHT]]);
-        newConfig->setAttribute(Attribute::DpiX,
-                values[attributeMap[HWC_DISPLAY_DPI_X]]);
-        newConfig->setAttribute(Attribute::DpiY,
-                values[attributeMap[HWC_DISPLAY_DPI_Y]]);
-        if (hasColor) {
-            // In HWC1, color modes are referred to as color transforms. To avoid confusion with
-            // the HWC2 concept of color transforms, we internally refer to them as color modes for
-            // both HWC1 and 2.
-            newConfig->setAttribute(ColorMode,
-                    values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
-        }
-
-        // We can only do this after attempting to read the color mode
-        newConfig->setHwc1Id(hwc1ConfigId);
-
-        for (auto& existingConfig : mConfigs) {
-            if (existingConfig->merge(*newConfig)) {
-                ALOGV("Merged config %d with existing config %u: %s",
-                        hwc1ConfigId, existingConfig->getId(),
-                        existingConfig->toString().c_str());
-                newConfig.reset();
-                break;
-            }
-        }
-
-        // If it wasn't merged with any existing config, add it to the end
-        if (newConfig) {
-            newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
-            ALOGV("Found new config %u: %s", newConfig->getId(),
-                    newConfig->toString().c_str());
-            mConfigs.emplace_back(std::move(newConfig));
-        }
-    }
-
-    initializeActiveConfig();
-    populateColorModes();
-}
-
-void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    mConfigs.emplace_back(std::make_shared<Config>(*this));
-    auto& config = mConfigs[0];
-
-    config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
-    config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
-    config->setHwc1Id(0);
-    config->setId(0);
-    mActiveConfig = config;
-}
-
-bool HWC2On1Adapter::Display::prepare()
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    // Only prepare display contents for displays HWC1 knows about
-    if (mHwc1Id == -1) {
-        return true;
-    }
-
-    // It doesn't make sense to prepare a display for which there is no active
-    // config, so return early
-    if (!mActiveConfig) {
-        ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId);
-        return false;
-    }
-
-    ALOGV("[%" PRIu64 "] Entering prepare", mId);
-
-    auto currentCount = mHwc1RequestedContents ?
-            mHwc1RequestedContents->numHwLayers : 0;
-    auto requiredCount = mLayers.size() + 1;
-    ALOGV("[%" PRIu64 "]   Requires %zd layers, %zd allocated in %p", mId,
-            requiredCount, currentCount, mHwc1RequestedContents.get());
-
-    bool layerCountChanged = (currentCount != requiredCount);
-    if (layerCountChanged) {
-        reallocateHwc1Contents();
-    }
-
-    bool applyAllState = false;
-    if (layerCountChanged || mZIsDirty) {
-        assignHwc1LayerIds();
-        mZIsDirty = false;
-        applyAllState = true;
-    }
-
-    mHwc1RequestedContents->retireFenceFd = -1;
-    mHwc1RequestedContents->flags = 0;
-    if (isDirty() || applyAllState) {
-        mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
-    }
-
-    for (auto& layer : mLayers) {
-        auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
-        hwc1Layer.releaseFenceFd = -1;
-        layer->applyState(hwc1Layer, applyAllState);
-    }
-
-    mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
-    mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
-
-    prepareFramebufferTarget();
-
-    return true;
-}
-
-static void cloneHWCRegion(hwc_region_t& region)
-{
-    auto size = sizeof(hwc_rect_t) * region.numRects;
-    auto newRects = static_cast<hwc_rect_t*>(std::malloc(size));
-    std::copy_n(region.rects, region.numRects, newRects);
-    region.rects = newRects;
-}
-
-HWC2On1Adapter::Display::HWC1Contents
-        HWC2On1Adapter::Display::cloneRequestedContents() const
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    size_t size = sizeof(hwc_display_contents_1_t) +
-            sizeof(hwc_layer_1_t) * (mHwc1RequestedContents->numHwLayers);
-    auto contents = static_cast<hwc_display_contents_1_t*>(std::malloc(size));
-    std::memcpy(contents, mHwc1RequestedContents.get(), size);
-    for (size_t layerId = 0; layerId < contents->numHwLayers; ++layerId) {
-        auto& layer = contents->hwLayers[layerId];
-        // Deep copy the regions to avoid double-frees
-        cloneHWCRegion(layer.visibleRegionScreen);
-        cloneHWCRegion(layer.surfaceDamage);
-    }
-    return HWC1Contents(contents);
-}
-
-void HWC2On1Adapter::Display::setReceivedContents(HWC1Contents contents)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    mHwc1ReceivedContents = std::move(contents);
-
-    mChanges.reset(new Changes);
-
-    size_t numLayers = mHwc1ReceivedContents->numHwLayers;
-    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
-        const auto& receivedLayer = mHwc1ReceivedContents->hwLayers[hwc1Id];
-        if (mHwc1LayerMap.count(hwc1Id) == 0) {
-            ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
-                    "setReceivedContents: HWC1 layer %zd doesn't have a"
-                    " matching HWC2 layer, and isn't the framebuffer target",
-                    hwc1Id);
-            continue;
-        }
-
-        Layer& layer = *mHwc1LayerMap[hwc1Id];
-        updateTypeChanges(receivedLayer, layer);
-        updateLayerRequests(receivedLayer, layer);
-    }
-}
-
-bool HWC2On1Adapter::Display::hasChanges() const
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-    return mChanges != nullptr;
-}
-
-Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    if (!mChanges || (mChanges->getNumTypes() > 0)) {
-        ALOGE("[%" PRIu64 "] set failed: not validated", mId);
-        return Error::NotValidated;
-    }
-
-    // Set up the client/framebuffer target
-    auto numLayers = hwcContents.numHwLayers;
-
-    // Close acquire fences on FRAMEBUFFER layers, since they will not be used
-    // by HWC
-    for (size_t l = 0; l < numLayers - 1; ++l) {
-        auto& layer = hwcContents.hwLayers[l];
-        if (layer.compositionType == HWC_FRAMEBUFFER) {
-            ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l);
-            close(layer.acquireFenceFd);
-            layer.acquireFenceFd = -1;
-        }
-    }
-
-    auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1];
-    if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) {
-        clientTargetLayer.handle = mClientTarget.getBuffer();
-        clientTargetLayer.acquireFenceFd = mClientTarget.getFence();
-    } else {
-        ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET",
-                mId);
-    }
-
-    mChanges.reset();
-
-    return Error::None;
-}
-
-void HWC2On1Adapter::Display::addRetireFence(int fenceFd)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-    mRetireFence.add(fenceFd);
-}
-
-void HWC2On1Adapter::Display::addReleaseFences(
-        const hwc_display_contents_1_t& hwcContents)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    size_t numLayers = hwcContents.numHwLayers;
-    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
-        const auto& receivedLayer = hwcContents.hwLayers[hwc1Id];
-        if (mHwc1LayerMap.count(hwc1Id) == 0) {
-            if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) {
-                ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a"
-                        " matching HWC2 layer, and isn't the framebuffer"
-                        " target", hwc1Id);
-            }
-            // Close the framebuffer target release fence since we will use the
-            // display retire fence instead
-            if (receivedLayer.releaseFenceFd != -1) {
-                close(receivedLayer.releaseFenceFd);
-            }
-            continue;
-        }
-
-        Layer& layer = *mHwc1LayerMap[hwc1Id];
-        ALOGV("Adding release fence %d to layer %" PRIu64,
-                receivedLayer.releaseFenceFd, layer.getId());
-        layer.addReleaseFence(receivedLayer.releaseFenceFd);
-    }
-}
-
-bool HWC2On1Adapter::Display::hasColorTransform() const
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-    return mHasColorTransform;
-}
-
-static std::string hwc1CompositionString(int32_t type)
-{
-    switch (type) {
-        case HWC_FRAMEBUFFER: return "Framebuffer";
-        case HWC_OVERLAY: return "Overlay";
-        case HWC_BACKGROUND: return "Background";
-        case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget";
-        case HWC_SIDEBAND: return "Sideband";
-        case HWC_CURSOR_OVERLAY: return "CursorOverlay";
-        default:
-            return std::string("Unknown (") + std::to_string(type) + ")";
-    }
-}
-
-static std::string hwc1TransformString(int32_t transform)
-{
-    switch (transform) {
-        case 0: return "None";
-        case HWC_TRANSFORM_FLIP_H: return "FlipH";
-        case HWC_TRANSFORM_FLIP_V: return "FlipV";
-        case HWC_TRANSFORM_ROT_90: return "Rotate90";
-        case HWC_TRANSFORM_ROT_180: return "Rotate180";
-        case HWC_TRANSFORM_ROT_270: return "Rotate270";
-        case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90";
-        case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90";
-        default:
-            return std::string("Unknown (") + std::to_string(transform) + ")";
-    }
-}
-
-static std::string hwc1BlendModeString(int32_t mode)
-{
-    switch (mode) {
-        case HWC_BLENDING_NONE: return "None";
-        case HWC_BLENDING_PREMULT: return "Premultiplied";
-        case HWC_BLENDING_COVERAGE: return "Coverage";
-        default:
-            return std::string("Unknown (") + std::to_string(mode) + ")";
-    }
-}
-
-static std::string rectString(hwc_rect_t rect)
-{
-    std::stringstream output;
-    output << "[" << rect.left << ", " << rect.top << ", ";
-    output << rect.right << ", " << rect.bottom << "]";
-    return output.str();
-}
-
-static std::string approximateFloatString(float f)
-{
-    if (static_cast<int32_t>(f) == f) {
-        return std::to_string(static_cast<int32_t>(f));
-    }
-    int32_t truncated = static_cast<int32_t>(f * 10);
-    bool approximate = (static_cast<float>(truncated) != f * 10);
-    const size_t BUFFER_SIZE = 32;
-    char buffer[BUFFER_SIZE] = {};
-    auto bytesWritten = snprintf(buffer, BUFFER_SIZE,
-            "%s%.1f", approximate ? "~" : "", f);
-    return std::string(buffer, bytesWritten);
-}
-
-static std::string frectString(hwc_frect_t frect)
-{
-    std::stringstream output;
-    output << "[" << approximateFloatString(frect.left) << ", ";
-    output << approximateFloatString(frect.top) << ", ";
-    output << approximateFloatString(frect.right) << ", ";
-    output << approximateFloatString(frect.bottom) << "]";
-    return output.str();
-}
-
-static std::string colorString(hwc_color_t color)
-{
-    std::stringstream output;
-    output << "RGBA [";
-    output << static_cast<int32_t>(color.r) << ", ";
-    output << static_cast<int32_t>(color.g) << ", ";
-    output << static_cast<int32_t>(color.b) << ", ";
-    output << static_cast<int32_t>(color.a) << "]";
-    return output.str();
-}
-
-static std::string alphaString(float f)
-{
-    const size_t BUFFER_SIZE = 8;
-    char buffer[BUFFER_SIZE] = {};
-    auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
-    return std::string(buffer, bytesWritten);
-}
-
-static std::string to_string(const hwc_layer_1_t& hwcLayer,
-        int32_t hwc1MinorVersion)
-{
-    const char* fill = "          ";
-
-    std::stringstream output;
-
-    output << "  Composition: " <<
-            hwc1CompositionString(hwcLayer.compositionType);
-
-    if (hwcLayer.compositionType == HWC_BACKGROUND) {
-        output << "  Color: " << colorString(hwcLayer.backgroundColor) << '\n';
-    } else if (hwcLayer.compositionType == HWC_SIDEBAND) {
-        output << "  Stream: " << hwcLayer.sidebandStream << '\n';
-    } else {
-        output << "  Buffer: " << hwcLayer.handle << "/" <<
-                hwcLayer.acquireFenceFd << '\n';
-    }
-
-    output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) <<
-            '\n';
-
-    output << fill << "Source crop: ";
-    if (hwc1MinorVersion >= 3) {
-        output << frectString(hwcLayer.sourceCropf) << '\n';
-    } else {
-        output << rectString(hwcLayer.sourceCropi) << '\n';
-    }
-
-    output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform);
-    output << "  Blend mode: " << hwc1BlendModeString(hwcLayer.blending);
-    if (hwcLayer.planeAlpha != 0xFF) {
-        output << "  Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f);
-    }
-    output << '\n';
-
-    if (hwcLayer.hints != 0) {
-        output << fill << "Hints:";
-        if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) {
-            output << " TripleBuffer";
-        }
-        if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) {
-            output << " ClearFB";
-        }
-        output << '\n';
-    }
-
-    if (hwcLayer.flags != 0) {
-        output << fill << "Flags:";
-        if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) {
-            output << " SkipLayer";
-        }
-        if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) {
-            output << " IsCursorLayer";
-        }
-        output << '\n';
-    }
-
-    return output.str();
-}
-
-static std::string to_string(const hwc_display_contents_1_t& hwcContents,
-        int32_t hwc1MinorVersion)
-{
-    const char* fill = "      ";
-
-    std::stringstream output;
-    output << fill << "Geometry changed: " <<
-            ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n");
-
-    output << fill << hwcContents.numHwLayers << " Layer" <<
-            ((hwcContents.numHwLayers == 1) ? "\n" : "s\n");
-    for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) {
-        output << fill << "  Layer " << layer;
-        output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion);
-    }
-
-    if (hwcContents.outbuf != nullptr) {
-        output << fill << "Output buffer: " << hwcContents.outbuf << "/" <<
-                hwcContents.outbufAcquireFenceFd << '\n';
-    }
-
-    return output.str();
-}
-
-std::string HWC2On1Adapter::Display::dump() const
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    std::stringstream output;
-
-    output << "  Display " << mId << ": ";
-    output << to_string(mType) << "  ";
-    output << "HWC1 ID: " << mHwc1Id << "  ";
-    output << "Power mode: " << to_string(mPowerMode) << "  ";
-    output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
-
-    output << "    Color modes [active]:";
-    for (const auto& mode : mColorModes) {
-        if (mode == mActiveColorMode) {
-            output << " [" << mode << ']';
-        } else {
-            output << " " << mode;
-        }
-    }
-    output << '\n';
-
-    output << "    " << mConfigs.size() << " Config" <<
-            (mConfigs.size() == 1 ? "" : "s") << " (* active)\n";
-    for (const auto& config : mConfigs) {
-        output << (config == mActiveConfig ? "    * " : "      ");
-        output << config->toString(true) << '\n';
-    }
-
-    output << "    " << mLayers.size() << " Layer" <<
-            (mLayers.size() == 1 ? "" : "s") << '\n';
-    for (const auto& layer : mLayers) {
-        output << layer->dump();
-    }
-
-    output << "    Client target: " << mClientTarget.getBuffer() << '\n';
-
-    if (mOutputBuffer.getBuffer() != nullptr) {
-        output << "    Output buffer: " << mOutputBuffer.getBuffer() << '\n';
-    }
-
-    if (mHwc1ReceivedContents) {
-        output << "    Last received HWC1 state\n";
-        output << to_string(*mHwc1ReceivedContents, mDevice.mHwc1MinorVersion);
-    } else if (mHwc1RequestedContents) {
-        output << "    Last requested HWC1 state\n";
-        output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
-    }
-
-    return output.str();
-}
-
-void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
-        int32_t value)
-{
-    mAttributes[attribute] = value;
-}
-
-int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const
-{
-    if (mAttributes.count(attribute) == 0) {
-        return -1;
-    }
-    return mAttributes.at(attribute);
-}
-
-void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id)
-{
-    android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
-    mHwc1Ids.emplace(colorMode, id);
-}
-
-bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const
-{
-    for (const auto& idPair : mHwc1Ids) {
-        if (id == idPair.second) {
-            return true;
-        }
-    }
-    return false;
-}
-
-Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
-        uint32_t id, android_color_mode_t* outMode) const
-{
-    for (const auto& idPair : mHwc1Ids) {
-        if (id == idPair.second) {
-            *outMode = idPair.first;
-            return Error::None;
-        }
-    }
-    ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId);
-    return Error::BadParameter;
-}
-
-Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
-        uint32_t* outId) const
-{
-    for (const auto& idPair : mHwc1Ids) {
-        if (mode == idPair.first) {
-            *outId = idPair.second;
-            return Error::None;
-        }
-    }
-    ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId);
-    return Error::BadParameter;
-}
-
-bool HWC2On1Adapter::Display::Config::merge(const Config& other)
-{
-    auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
-            HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
-            HWC2::Attribute::DpiY};
-    for (auto attribute : attributes) {
-        if (getAttribute(attribute) != other.getAttribute(attribute)) {
-            return false;
-        }
-    }
-    android_color_mode_t otherColorMode =
-            static_cast<android_color_mode_t>(other.getAttribute(ColorMode));
-    if (mHwc1Ids.count(otherColorMode) != 0) {
-        ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
-                "identical", mHwc1Ids.at(otherColorMode),
-                other.mHwc1Ids.at(otherColorMode));
-        return false;
-    }
-    mHwc1Ids.emplace(otherColorMode,
-            other.mHwc1Ids.at(otherColorMode));
-    return true;
-}
-
-std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const
-{
-    std::set<android_color_mode_t> colorModes;
-    for (const auto& idPair : mHwc1Ids) {
-        colorModes.emplace(idPair.first);
-    }
-    return colorModes;
-}
-
-std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const
-{
-    std::string output;
-
-    const size_t BUFFER_SIZE = 100;
-    char buffer[BUFFER_SIZE] = {};
-    auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
-            "%u x %u", mAttributes.at(HWC2::Attribute::Width),
-            mAttributes.at(HWC2::Attribute::Height));
-    output.append(buffer, writtenBytes);
-
-    if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) {
-        std::memset(buffer, 0, BUFFER_SIZE);
-        writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz",
-                1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod));
-        output.append(buffer, writtenBytes);
-    }
-
-    if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
-            mAttributes.at(HWC2::Attribute::DpiX) != -1) {
-        std::memset(buffer, 0, BUFFER_SIZE);
-        writtenBytes = snprintf(buffer, BUFFER_SIZE,
-                ", DPI: %.1f x %.1f",
-                mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
-                mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
-        output.append(buffer, writtenBytes);
-    }
-
-    std::memset(buffer, 0, BUFFER_SIZE);
-    if (splitLine) {
-        writtenBytes = snprintf(buffer, BUFFER_SIZE,
-                "\n        HWC1 ID/Color transform:");
-    } else {
-        writtenBytes = snprintf(buffer, BUFFER_SIZE,
-                ", HWC1 ID/Color transform:");
-    }
-    output.append(buffer, writtenBytes);
-
-
-    for (const auto& id : mHwc1Ids) {
-        android_color_mode_t colorMode = id.first;
-        uint32_t hwc1Id = id.second;
-        std::memset(buffer, 0, BUFFER_SIZE);
-        if (colorMode == mDisplay.mActiveColorMode) {
-            writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
-                    colorMode);
-        } else {
-            writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
-                    colorMode);
-        }
-        output.append(buffer, writtenBytes);
-    }
-
-    return output;
-}
-
-std::shared_ptr<const HWC2On1Adapter::Display::Config>
-        HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const
-{
-    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
-        return nullptr;
-    }
-    return mConfigs[configId];
-}
-
-void HWC2On1Adapter::Display::populateColorModes()
-{
-    mColorModes = mConfigs[0]->getColorModes();
-    for (const auto& config : mConfigs) {
-        std::set<android_color_mode_t> intersection;
-        auto configModes = config->getColorModes();
-        std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
-                configModes.cbegin(), configModes.cend(),
-                std::inserter(intersection, intersection.begin()));
-        std::swap(intersection, mColorModes);
-    }
-}
-
-void HWC2On1Adapter::Display::initializeActiveConfig()
-{
-    if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
-        ALOGV("getActiveConfig is null, choosing config 0");
-        mActiveConfig = mConfigs[0];
-        mActiveColorMode = HAL_COLOR_MODE_NATIVE;
-        return;
-    }
-
-    auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
-            mDevice.mHwc1Device, mHwc1Id);
-    if (activeConfig >= 0) {
-        for (const auto& config : mConfigs) {
-            if (config->hasHwc1Id(activeConfig)) {
-                ALOGV("Setting active config to %d for HWC1 config %u",
-                        config->getId(), activeConfig);
-                mActiveConfig = config;
-                if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
-                    // This should never happen since we checked for the config's presence before
-                    // setting it as active.
-                    ALOGE("Unable to find color mode for active HWC1 config %d",
-                            config->getId());
-                    mActiveColorMode = HAL_COLOR_MODE_NATIVE;
-                }
-                break;
-            }
-        }
-        if (!mActiveConfig) {
-            ALOGV("Unable to find active HWC1 config %u, defaulting to "
-                    "config 0", activeConfig);
-            mActiveConfig = mConfigs[0];
-            mActiveColorMode = HAL_COLOR_MODE_NATIVE;
-        }
-    }
-}
-
-void HWC2On1Adapter::Display::reallocateHwc1Contents()
-{
-    // Allocate an additional layer for the framebuffer target
-    auto numLayers = mLayers.size() + 1;
-    size_t size = sizeof(hwc_display_contents_1_t) +
-            sizeof(hwc_layer_1_t) * numLayers;
-    ALOGV("[%" PRIu64 "] reallocateHwc1Contents creating %zd layer%s", mId,
-            numLayers, numLayers != 1 ? "s" : "");
-    auto contents =
-            static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
-    contents->numHwLayers = numLayers;
-    mHwc1RequestedContents.reset(contents);
-}
-
-void HWC2On1Adapter::Display::assignHwc1LayerIds()
-{
-    mHwc1LayerMap.clear();
-    size_t nextHwc1Id = 0;
-    for (auto& layer : mLayers) {
-        mHwc1LayerMap[nextHwc1Id] = layer;
-        layer->setHwc1Id(nextHwc1Id++);
-    }
-}
-
-void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
-        const Layer& layer)
-{
-    auto layerId = layer.getId();
-    switch (hwc1Layer.compositionType) {
-        case HWC_FRAMEBUFFER:
-            if (layer.getCompositionType() != Composition::Client) {
-                mChanges->addTypeChange(layerId, Composition::Client);
-            }
-            break;
-        case HWC_OVERLAY:
-            if (layer.getCompositionType() != Composition::Device) {
-                mChanges->addTypeChange(layerId, Composition::Device);
-            }
-            break;
-        case HWC_BACKGROUND:
-            ALOGE_IF(layer.getCompositionType() != Composition::SolidColor,
-                    "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2"
-                    " wasn't expecting SolidColor");
-            break;
-        case HWC_FRAMEBUFFER_TARGET:
-            // Do nothing, since it shouldn't be modified by HWC1
-            break;
-        case HWC_SIDEBAND:
-            ALOGE_IF(layer.getCompositionType() != Composition::Sideband,
-                    "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2"
-                    " wasn't expecting Sideband");
-            break;
-        case HWC_CURSOR_OVERLAY:
-            ALOGE_IF(layer.getCompositionType() != Composition::Cursor,
-                    "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but"
-                    " HWC2 wasn't expecting Cursor");
-            break;
-    }
-}
-
-void HWC2On1Adapter::Display::updateLayerRequests(
-        const hwc_layer_1_t& hwc1Layer, const Layer& layer)
-{
-    if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
-        mChanges->addLayerRequest(layer.getId(),
-                LayerRequest::ClearClientTarget);
-    }
-}
-
-void HWC2On1Adapter::Display::prepareFramebufferTarget()
-{
-    // We check that mActiveConfig is valid in Display::prepare
-    int32_t width = mActiveConfig->getAttribute(Attribute::Width);
-    int32_t height = mActiveConfig->getAttribute(Attribute::Height);
-
-    auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()];
-    hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET;
-    hwc1Target.releaseFenceFd = -1;
-    hwc1Target.hints = 0;
-    hwc1Target.flags = 0;
-    hwc1Target.transform = 0;
-    hwc1Target.blending = HWC_BLENDING_PREMULT;
-    if (mDevice.getHwc1MinorVersion() < 3) {
-        hwc1Target.sourceCropi = {0, 0, width, height};
-    } else {
-        hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width),
-                static_cast<float>(height)};
-    }
-    hwc1Target.displayFrame = {0, 0, width, height};
-    hwc1Target.planeAlpha = 255;
-    hwc1Target.visibleRegionScreen.numRects = 1;
-    auto rects = static_cast<hwc_rect_t*>(std::malloc(sizeof(hwc_rect_t)));
-    rects[0].left = 0;
-    rects[0].top = 0;
-    rects[0].right = width;
-    rects[0].bottom = height;
-    hwc1Target.visibleRegionScreen.rects = rects;
-
-    // We will set this to the correct value in set
-    hwc1Target.acquireFenceFd = -1;
-}
-
-// Layer functions
-
-std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1);
-
-HWC2On1Adapter::Layer::Layer(Display& display)
-  : mId(sNextId++),
-    mDisplay(display),
-    mDirtyCount(0),
-    mBuffer(),
-    mSurfaceDamage(),
-    mBlendMode(*this, BlendMode::None),
-    mColor(*this, {0, 0, 0, 0}),
-    mCompositionType(*this, Composition::Invalid),
-    mDisplayFrame(*this, {0, 0, -1, -1}),
-    mPlaneAlpha(*this, 0.0f),
-    mSidebandStream(*this, nullptr),
-    mSourceCrop(*this, {0.0f, 0.0f, -1.0f, -1.0f}),
-    mTransform(*this, Transform::None),
-    mVisibleRegion(*this, std::vector<hwc_rect_t>()),
-    mZ(0),
-    mReleaseFence(),
-    mHwc1Id(0),
-    mHasUnsupportedDataspace(false),
-    mHasUnsupportedPlaneAlpha(false) {}
-
-bool HWC2On1Adapter::SortLayersByZ::operator()(
-        const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs)
-{
-    return lhs->getZ() < rhs->getZ();
-}
-
-Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
-        int32_t acquireFence)
-{
-    ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
-    mBuffer.setBuffer(buffer);
-    mBuffer.setFence(acquireFence);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y)
-{
-    if (mCompositionType.getValue() != Composition::Cursor) {
-        return Error::BadLayer;
-    }
-
-    if (mDisplay.hasChanges()) {
-        return Error::NotValidated;
-    }
-
-    auto displayId = mDisplay.getHwc1Id();
-    auto hwc1Device = mDisplay.getDevice().getHwc1Device();
-    hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage)
-{
-    mSurfaceDamage.resize(damage.numRects);
-    std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
-    return Error::None;
-}
-
-// Layer state functions
-
-Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode)
-{
-    mBlendMode.setPending(mode);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setColor(hwc_color_t color)
-{
-    mColor.setPending(color);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setCompositionType(Composition type)
-{
-    mCompositionType.setPending(type);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t dataspace)
-{
-    mHasUnsupportedDataspace = (dataspace != HAL_DATASPACE_UNKNOWN);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame)
-{
-    mDisplayFrame.setPending(frame);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha)
-{
-    mPlaneAlpha.setPending(alpha);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream)
-{
-    mSidebandStream.setPending(stream);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop)
-{
-    mSourceCrop.setPending(crop);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setTransform(Transform transform)
-{
-    mTransform.setPending(transform);
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t rawVisible)
-{
-    std::vector<hwc_rect_t> visible(rawVisible.rects,
-            rawVisible.rects + rawVisible.numRects);
-    mVisibleRegion.setPending(std::move(visible));
-    return Error::None;
-}
-
-Error HWC2On1Adapter::Layer::setZ(uint32_t z)
-{
-    mZ = z;
-    return Error::None;
-}
-
-void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd)
-{
-    ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
-    mReleaseFence.add(fenceFd);
-}
-
-const sp<Fence>& HWC2On1Adapter::Layer::getReleaseFence() const
-{
-    return mReleaseFence.get();
-}
-
-void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    applyCommonState(hwc1Layer, applyAllState);
-    auto compositionType = mCompositionType.getPendingValue();
-    if (compositionType == Composition::SolidColor) {
-        applySolidColorState(hwc1Layer, applyAllState);
-    } else if (compositionType == Composition::Sideband) {
-        applySidebandState(hwc1Layer, applyAllState);
-    } else {
-        applyBufferState(hwc1Layer);
-    }
-    applyCompositionType(hwc1Layer, applyAllState);
-}
-
-// Layer dump helpers
-
-static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
-        const std::vector<hwc_rect_t>& surfaceDamage)
-{
-    std::string regions;
-    regions += "        Visible Region";
-    regions.resize(40, ' ');
-    regions += "Surface Damage\n";
-
-    size_t numPrinted = 0;
-    size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size());
-    while (numPrinted < maxSize) {
-        std::string line("        ");
-        if (visibleRegion.empty() && numPrinted == 0) {
-            line += "None";
-        } else if (numPrinted < visibleRegion.size()) {
-            line += rectString(visibleRegion[numPrinted]);
-        }
-        line.resize(40, ' ');
-        if (surfaceDamage.empty() && numPrinted == 0) {
-            line += "None";
-        } else if (numPrinted < surfaceDamage.size()) {
-            line += rectString(surfaceDamage[numPrinted]);
-        }
-        line += '\n';
-        regions += line;
-        ++numPrinted;
-    }
-    return regions;
-}
-
-std::string HWC2On1Adapter::Layer::dump() const
-{
-    std::stringstream output;
-    const char* fill = "      ";
-
-    output << fill << to_string(mCompositionType.getPendingValue());
-    output << " Layer  HWC2/1: " << mId << "/" << mHwc1Id << "  ";
-    output << "Z: " << mZ;
-    if (mCompositionType.getValue() == HWC2::Composition::SolidColor) {
-        output << "  " << colorString(mColor.getValue());
-    } else if (mCompositionType.getValue() == HWC2::Composition::Sideband) {
-        output << "  Handle: " << mSidebandStream.getValue() << '\n';
-    } else {
-        output << "  Buffer: " << mBuffer.getBuffer() << "/" <<
-                mBuffer.getFence() << '\n';
-        output << fill << "  Display frame [LTRB]: " <<
-                rectString(mDisplayFrame.getValue()) << '\n';
-        output << fill << "  Source crop: " <<
-                frectString(mSourceCrop.getValue()) << '\n';
-        output << fill << "  Transform: " << to_string(mTransform.getValue());
-        output << "  Blend mode: " << to_string(mBlendMode.getValue());
-        if (mPlaneAlpha.getValue() != 1.0f) {
-            output << "  Alpha: " <<
-                alphaString(mPlaneAlpha.getValue()) << '\n';
-        } else {
-            output << '\n';
-        }
-        output << regionStrings(mVisibleRegion.getValue(), mSurfaceDamage);
-    }
-    return output.str();
-}
-
-static int getHwc1Blending(HWC2::BlendMode blendMode)
-{
-    switch (blendMode) {
-        case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
-        case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
-        default: return HWC_BLENDING_NONE;
-    }
-}
-
-void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
-    if (applyAllState || mBlendMode.isDirty()) {
-        hwc1Layer.blending = getHwc1Blending(mBlendMode.getPendingValue());
-        mBlendMode.latch();
-    }
-    if (applyAllState || mDisplayFrame.isDirty()) {
-        hwc1Layer.displayFrame = mDisplayFrame.getPendingValue();
-        mDisplayFrame.latch();
-    }
-    if (applyAllState || mPlaneAlpha.isDirty()) {
-        auto pendingAlpha = mPlaneAlpha.getPendingValue();
-        if (minorVersion < 2) {
-            mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
-        } else {
-            hwc1Layer.planeAlpha =
-                    static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
-        }
-        mPlaneAlpha.latch();
-    }
-    if (applyAllState || mSourceCrop.isDirty()) {
-        if (minorVersion < 3) {
-            auto pending = mSourceCrop.getPendingValue();
-            hwc1Layer.sourceCropi.left =
-                    static_cast<int32_t>(std::ceil(pending.left));
-            hwc1Layer.sourceCropi.top =
-                    static_cast<int32_t>(std::ceil(pending.top));
-            hwc1Layer.sourceCropi.right =
-                    static_cast<int32_t>(std::floor(pending.right));
-            hwc1Layer.sourceCropi.bottom =
-                    static_cast<int32_t>(std::floor(pending.bottom));
-        } else {
-            hwc1Layer.sourceCropf = mSourceCrop.getPendingValue();
-        }
-        mSourceCrop.latch();
-    }
-    if (applyAllState || mTransform.isDirty()) {
-        hwc1Layer.transform =
-                static_cast<uint32_t>(mTransform.getPendingValue());
-        mTransform.latch();
-    }
-    if (applyAllState || mVisibleRegion.isDirty()) {
-        auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
-
-        std::free(const_cast<hwc_rect_t*>(hwc1VisibleRegion.rects));
-
-        auto pending = mVisibleRegion.getPendingValue();
-        hwc_rect_t* newRects = static_cast<hwc_rect_t*>(
-                std::malloc(sizeof(hwc_rect_t) * pending.size()));
-        std::copy(pending.begin(), pending.end(), newRects);
-        hwc1VisibleRegion.rects = const_cast<const hwc_rect_t*>(newRects);
-        hwc1VisibleRegion.numRects = pending.size();
-        mVisibleRegion.latch();
-    }
-}
-
-void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    if (applyAllState || mColor.isDirty()) {
-        hwc1Layer.backgroundColor = mColor.getPendingValue();
-        mColor.latch();
-    }
-}
-
-void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    if (applyAllState || mSidebandStream.isDirty()) {
-        hwc1Layer.sidebandStream = mSidebandStream.getPendingValue();
-        mSidebandStream.latch();
-    }
-}
-
-void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer)
-{
-    hwc1Layer.handle = mBuffer.getBuffer();
-    hwc1Layer.acquireFenceFd = mBuffer.getFence();
-}
-
-void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    // HWC1 never supports color transforms or dataspaces and only sometimes
-    // supports plane alpha (depending on the version). These require us to drop
-    // some or all layers to client composition.
-    if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
-            mDisplay.hasColorTransform()) {
-        hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-        hwc1Layer.flags = HWC_SKIP_LAYER;
-        return;
-    }
-
-    if (applyAllState || mCompositionType.isDirty()) {
-        hwc1Layer.flags = 0;
-        switch (mCompositionType.getPendingValue()) {
-            case Composition::Client:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-            case Composition::Device:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                break;
-            case Composition::SolidColor:
-                // In theory the following line should work, but since the HWC1
-                // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
-                // devices may not work correctly. To be on the safe side, we
-                // fall back to client composition.
-                //
-                // hwc1Layer.compositionType = HWC_BACKGROUND;
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-            case Composition::Cursor:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
-                    hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
-                }
-                break;
-            case Composition::Sideband:
-                if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
-                    hwc1Layer.compositionType = HWC_SIDEBAND;
-                } else {
-                    hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                    hwc1Layer.flags |= HWC_SKIP_LAYER;
-                }
-                break;
-            default:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-        }
-        ALOGV("Layer %" PRIu64 " %s set to %d", mId,
-                to_string(mCompositionType.getPendingValue()).c_str(),
-                hwc1Layer.compositionType);
-        ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, "    and skipping");
-        mCompositionType.latch();
-    }
-}
-
-// Adapter helpers
-
-void HWC2On1Adapter::populateCapabilities()
-{
-    ALOGV("populateCapabilities");
-    if (mHwc1MinorVersion >= 3U) {
-        int supportedTypes = 0;
-        auto result = mHwc1Device->query(mHwc1Device,
-                HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
-        if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
-            ALOGI("Found support for HWC virtual displays");
-            mHwc1SupportsVirtualDisplays = true;
-        }
-    }
-    if (mHwc1MinorVersion >= 4U) {
-        mCapabilities.insert(Capability::SidebandStream);
-    }
-}
-
-HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id)
-{
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    auto display = mDisplays.find(id);
-    if (display == mDisplays.end()) {
-        return nullptr;
-    }
-
-    return display->second.get();
-}
-
-std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
-        hwc2_display_t displayId, hwc2_layer_t layerId)
-{
-    auto display = getDisplay(displayId);
-    if (!display) {
-        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
-    }
-
-    auto layerEntry = mLayers.find(layerId);
-    if (layerEntry == mLayers.end()) {
-        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
-    }
-
-    auto layer = layerEntry->second;
-    if (layer->getDisplay().getId() != displayId) {
-        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
-    }
-    return std::make_tuple(layer.get(), Error::None);
-}
-
-void HWC2On1Adapter::populatePrimary()
-{
-    ALOGV("populatePrimary");
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    auto display =
-            std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
-    mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
-    display->setHwc1Id(HWC_DISPLAY_PRIMARY);
-    display->populateConfigs();
-    mDisplays.emplace(display->getId(), std::move(display));
-}
-
-bool HWC2On1Adapter::prepareAllDisplays()
-{
-    ATRACE_CALL();
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    for (const auto& displayPair : mDisplays) {
-        auto& display = displayPair.second;
-        if (!display->prepare()) {
-            return false;
-        }
-    }
-
-    if (mHwc1DisplayMap.count(0) == 0) {
-        ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
-        return false;
-    }
-
-    // Always push the primary display
-    std::vector<HWC2On1Adapter::Display::HWC1Contents> requestedContents;
-    auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
-    auto& primaryDisplay = mDisplays[primaryDisplayId];
-    auto primaryDisplayContents = primaryDisplay->cloneRequestedContents();
-    requestedContents.push_back(std::move(primaryDisplayContents));
-
-    // Push the external display, if present
-    if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
-        auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
-        auto& externalDisplay = mDisplays[externalDisplayId];
-        auto externalDisplayContents =
-                externalDisplay->cloneRequestedContents();
-        requestedContents.push_back(std::move(externalDisplayContents));
-    } else {
-        // Even if an external display isn't present, we still need to send
-        // at least two displays down to HWC1
-        requestedContents.push_back(nullptr);
-    }
-
-    // Push the hardware virtual display, if supported and present
-    if (mHwc1MinorVersion >= 3) {
-        if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
-            auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
-            auto& virtualDisplay = mDisplays[virtualDisplayId];
-            auto virtualDisplayContents =
-                    virtualDisplay->cloneRequestedContents();
-            requestedContents.push_back(std::move(virtualDisplayContents));
-        } else {
-            requestedContents.push_back(nullptr);
-        }
-    }
-
-    mHwc1Contents.clear();
-    for (auto& displayContents : requestedContents) {
-        mHwc1Contents.push_back(displayContents.get());
-        if (!displayContents) {
-            continue;
-        }
-
-        ALOGV("Display %zd layers:", mHwc1Contents.size() - 1);
-        for (size_t l = 0; l < displayContents->numHwLayers; ++l) {
-            auto& layer = displayContents->hwLayers[l];
-            ALOGV("  %zd: %d", l, layer.compositionType);
-        }
-    }
-
-    ALOGV("Calling HWC1 prepare");
-    {
-        ATRACE_NAME("HWC1 prepare");
-        mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(),
-                mHwc1Contents.data());
-    }
-
-    for (size_t c = 0; c < mHwc1Contents.size(); ++c) {
-        auto& contents = mHwc1Contents[c];
-        if (!contents) {
-            continue;
-        }
-        ALOGV("Display %zd layers:", c);
-        for (size_t l = 0; l < contents->numHwLayers; ++l) {
-            ALOGV("  %zd: %d", l, contents->hwLayers[l].compositionType);
-        }
-    }
-
-    // Return the received contents to their respective displays
-    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
-        if (mHwc1Contents[hwc1Id] == nullptr) {
-            continue;
-        }
-
-        auto displayId = mHwc1DisplayMap[hwc1Id];
-        auto& display = mDisplays[displayId];
-        display->setReceivedContents(std::move(requestedContents[hwc1Id]));
-    }
-
-    return true;
-}
-
-Error HWC2On1Adapter::setAllDisplays()
-{
-    ATRACE_CALL();
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    // Make sure we're ready to validate
-    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
-        if (mHwc1Contents[hwc1Id] == nullptr) {
-            continue;
-        }
-
-        auto displayId = mHwc1DisplayMap[hwc1Id];
-        auto& display = mDisplays[displayId];
-        Error error = display->set(*mHwc1Contents[hwc1Id]);
-        if (error != Error::None) {
-            ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id,
-                    to_string(error).c_str());
-            return error;
-        }
-    }
-
-    ALOGV("Calling HWC1 set");
-    {
-        ATRACE_NAME("HWC1 set");
-        mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
-                mHwc1Contents.data());
-    }
-
-    // Add retire and release fences
-    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
-        if (mHwc1Contents[hwc1Id] == nullptr) {
-            continue;
-        }
-
-        auto displayId = mHwc1DisplayMap[hwc1Id];
-        auto& display = mDisplays[displayId];
-        auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd;
-        ALOGV("setAllDisplays: Adding retire fence %d to display %zd",
-                retireFenceFd, hwc1Id);
-        display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd);
-        display->addReleaseFences(*mHwc1Contents[hwc1Id]);
-    }
-
-    return Error::None;
-}
-
-void HWC2On1Adapter::hwc1Invalidate()
-{
-    ALOGV("Received hwc1Invalidate");
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    // If the HWC2-side callback hasn't been registered yet, buffer this until
-    // it is registered
-    if (mCallbacks.count(Callback::Refresh) == 0) {
-        mHasPendingInvalidate = true;
-        return;
-    }
-
-    const auto& callbackInfo = mCallbacks[Callback::Refresh];
-    std::vector<hwc2_display_t> displays;
-    for (const auto& displayPair : mDisplays) {
-        displays.emplace_back(displayPair.first);
-    }
-
-    // Call back without the state lock held
-    lock.unlock();
-
-    auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
-    for (auto display : displays) {
-        refresh(callbackInfo.data, display);
-    }
-}
-
-void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp)
-{
-    ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    // If the HWC2-side callback hasn't been registered yet, buffer this until
-    // it is registered
-    if (mCallbacks.count(Callback::Vsync) == 0) {
-        mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
-        return;
-    }
-
-    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
-        ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId);
-        return;
-    }
-
-    const auto& callbackInfo = mCallbacks[Callback::Vsync];
-    auto displayId = mHwc1DisplayMap[hwc1DisplayId];
-
-    // Call back without the state lock held
-    lock.unlock();
-
-    auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
-    vsync(callbackInfo.data, displayId, timestamp);
-}
-
-void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected)
-{
-    ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
-
-    if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
-        ALOGE("hwc1Hotplug: Received hotplug for non-external display");
-        return;
-    }
-
-    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
-
-    // If the HWC2-side callback hasn't been registered yet, buffer this until
-    // it is registered
-    if (mCallbacks.count(Callback::Hotplug) == 0) {
-        mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
-        return;
-    }
-
-    hwc2_display_t displayId = UINT64_MAX;
-    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
-        if (connected == 0) {
-            ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
-            return;
-        }
-
-        // Create a new display on connect
-        auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
-                HWC2::DisplayType::Physical);
-        display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
-        display->populateConfigs();
-        displayId = display->getId();
-        mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
-        mDisplays.emplace(displayId, std::move(display));
-    } else {
-        if (connected != 0) {
-            ALOGW("hwc1Hotplug: Received connect for previously connected "
-                    "display");
-            return;
-        }
-
-        // Disconnect an existing display
-        displayId = mHwc1DisplayMap[hwc1DisplayId];
-        mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
-        mDisplays.erase(displayId);
-    }
-
-    const auto& callbackInfo = mCallbacks[Callback::Hotplug];
-
-    // Call back without the state lock held
-    lock.unlock();
-
-    auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
-    auto hwc2Connected = (connected == 0) ?
-            HWC2::Connection::Disconnected : HWC2::Connection::Connected;
-    hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
deleted file mode 100644
index bdacc73..0000000
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H
-#define ANDROID_SF_HWC2_ON_1_ADAPTER_H
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <ui/Fence.h>
-
-#include <atomic>
-#include <map>
-#include <mutex>
-#include <queue>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-struct hwc_composer_device_1;
-struct hwc_display_contents_1;
-struct hwc_layer_1;
-
-namespace android {
-
-class HWC2On1Adapter : public hwc2_device_t
-{
-public:
-    HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device);
-    ~HWC2On1Adapter();
-
-    struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; }
-    uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; }
-
-private:
-    static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) {
-        return static_cast<HWC2On1Adapter*>(device);
-    }
-
-    // getCapabilities
-
-    void doGetCapabilities(uint32_t* outCount,
-            int32_t* /*hwc2_capability_t*/ outCapabilities);
-    static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount,
-            int32_t* /*hwc2_capability_t*/ outCapabilities) {
-        getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
-    }
-
-    // getFunction
-
-    hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
-    static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device,
-            int32_t intDesc) {
-        auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc);
-        return getAdapter(device)->doGetFunction(descriptor);
-    }
-
-    // Device functions
-
-    HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height,
-            hwc2_display_t* outDisplay);
-    static int32_t createVirtualDisplayHook(hwc2_device_t* device,
-            uint32_t width, uint32_t height, int32_t* /*format*/,
-            hwc2_display_t* outDisplay) {
-        // HWC1 implementations cannot override the buffer format requested by
-        // the consumer
-        auto error = getAdapter(device)->createVirtualDisplay(width, height,
-                outDisplay);
-        return static_cast<int32_t>(error);
-    }
-
-    HWC2::Error destroyVirtualDisplay(hwc2_display_t display);
-    static int32_t destroyVirtualDisplayHook(hwc2_device_t* device,
-            hwc2_display_t display) {
-        auto error = getAdapter(device)->destroyVirtualDisplay(display);
-        return static_cast<int32_t>(error);
-    }
-
-    std::string mDumpString;
-    void dump(uint32_t* outSize, char* outBuffer);
-    static void dumpHook(hwc2_device_t* device, uint32_t* outSize,
-            char* outBuffer) {
-        getAdapter(device)->dump(outSize, outBuffer);
-    }
-
-    uint32_t getMaxVirtualDisplayCount();
-    static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) {
-        return getAdapter(device)->getMaxVirtualDisplayCount();
-    }
-
-    HWC2::Error registerCallback(HWC2::Callback descriptor,
-            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer);
-    static int32_t registerCallbackHook(hwc2_device_t* device,
-            int32_t intDesc, hwc2_callback_data_t callbackData,
-            hwc2_function_pointer_t pointer) {
-        auto descriptor = static_cast<HWC2::Callback>(intDesc);
-        auto error = getAdapter(device)->registerCallback(descriptor,
-                callbackData, pointer);
-        return static_cast<int32_t>(error);
-    }
-
-    // Display functions
-
-    class Layer;
-
-    class SortLayersByZ {
-        public:
-            bool operator()(const std::shared_ptr<Layer>& lhs,
-                    const std::shared_ptr<Layer>& rhs);
-    };
-
-    class DisplayContentsDeleter {
-        public:
-            void operator()(struct hwc_display_contents_1* contents);
-    };
-
-    class DeferredFence {
-        public:
-            DeferredFence()
-              : mMutex(),
-                mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
-
-            void add(int32_t fenceFd) {
-                mFences.emplace(new Fence(fenceFd));
-                mFences.pop();
-            }
-
-            const sp<Fence>& get() const {
-                return mFences.front();
-            }
-
-        private:
-            mutable std::mutex mMutex;
-            std::queue<sp<Fence>> mFences;
-    };
-
-    class FencedBuffer {
-        public:
-            FencedBuffer() : mBuffer(nullptr), mFence(Fence::NO_FENCE) {}
-
-            void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
-            void setFence(int fenceFd) { mFence = new Fence(fenceFd); }
-
-            buffer_handle_t getBuffer() const { return mBuffer; }
-            int getFence() const { return mFence->dup(); }
-
-        private:
-            buffer_handle_t mBuffer;
-            sp<Fence> mFence;
-    };
-
-    class Display {
-        public:
-            typedef std::unique_ptr<hwc_display_contents_1,
-                    DisplayContentsDeleter> HWC1Contents;
-
-            Display(HWC2On1Adapter& device, HWC2::DisplayType type);
-
-            hwc2_display_t getId() const { return mId; }
-            HWC2On1Adapter& getDevice() const { return mDevice; }
-
-            // Does not require locking because it is set before adding the
-            // Displays to the Adapter's list of displays
-            void setHwc1Id(int32_t id) { mHwc1Id = id; }
-            int32_t getHwc1Id() const { return mHwc1Id; }
-
-            void incDirty() { ++mDirtyCount; }
-            void decDirty() { --mDirtyCount; }
-            bool isDirty() const { return mDirtyCount > 0 || mZIsDirty; }
-
-            // HWC2 Display functions
-            HWC2::Error acceptChanges();
-            HWC2::Error createLayer(hwc2_layer_t* outLayerId);
-            HWC2::Error destroyLayer(hwc2_layer_t layerId);
-            HWC2::Error getActiveConfig(hwc2_config_t* outConfigId);
-            HWC2::Error getAttribute(hwc2_config_t configId,
-                    HWC2::Attribute attribute, int32_t* outValue);
-            HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
-                    hwc2_layer_t* outLayers, int32_t* outTypes);
-            HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes);
-            HWC2::Error getConfigs(uint32_t* outNumConfigs,
-                    hwc2_config_t* outConfigIds);
-            HWC2::Error getDozeSupport(int32_t* outSupport);
-            HWC2::Error getHdrCapabilities(uint32_t* outNumTypes,
-                    int32_t* outTypes, float* outMaxLuminance,
-                    float* outMaxAverageLuminance, float* outMinLuminance);
-            HWC2::Error getName(uint32_t* outSize, char* outName);
-            HWC2::Error getReleaseFences(uint32_t* outNumElements,
-                    hwc2_layer_t* outLayers, int32_t* outFences);
-            HWC2::Error getRequests(int32_t* outDisplayRequests,
-                    uint32_t* outNumElements, hwc2_layer_t* outLayers,
-                    int32_t* outLayerRequests);
-            HWC2::Error getType(int32_t* outType);
-            HWC2::Error present(int32_t* outRetireFence);
-            HWC2::Error setActiveConfig(hwc2_config_t configId);
-            HWC2::Error setClientTarget(buffer_handle_t target,
-                    int32_t acquireFence, int32_t dataspace,
-                    hwc_region_t damage);
-            HWC2::Error setColorMode(android_color_mode_t mode);
-            HWC2::Error setColorTransform(android_color_transform_t hint);
-            HWC2::Error setOutputBuffer(buffer_handle_t buffer,
-                    int32_t releaseFence);
-            HWC2::Error setPowerMode(HWC2::PowerMode mode);
-            HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
-            HWC2::Error validate(uint32_t* outNumTypes,
-                    uint32_t* outNumRequests);
-
-            HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z);
-
-            // Read configs from HWC1 device
-            void populateConfigs();
-
-            // Set configs for a virtual display
-            void populateConfigs(uint32_t width, uint32_t height);
-
-            bool prepare();
-            HWC1Contents cloneRequestedContents() const;
-            void setReceivedContents(HWC1Contents contents);
-            bool hasChanges() const;
-            HWC2::Error set(hwc_display_contents_1& hwcContents);
-            void addRetireFence(int fenceFd);
-            void addReleaseFences(const hwc_display_contents_1& hwcContents);
-
-            bool hasColorTransform() const;
-
-            std::string dump() const;
-
-        private:
-            class Config {
-                public:
-                    Config(Display& display)
-                      : mDisplay(display),
-                        mAttributes() {}
-
-                    bool isOnDisplay(const Display& display) const {
-                        return display.getId() == mDisplay.getId();
-                    }
-
-                    void setAttribute(HWC2::Attribute attribute, int32_t value);
-                    int32_t getAttribute(HWC2::Attribute attribute) const;
-
-                    void setHwc1Id(uint32_t id);
-                    bool hasHwc1Id(uint32_t id) const;
-                    HWC2::Error getColorModeForHwc1Id(uint32_t id,
-                            android_color_mode_t *outMode) const;
-                    HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode,
-                            uint32_t* outId) const;
-
-                    void setId(hwc2_config_t id) { mId = id; }
-                    hwc2_config_t getId() const { return mId; }
-
-                    // Attempts to merge two configs that differ only in color
-                    // mode. Returns whether the merge was successful
-                    bool merge(const Config& other);
-
-                    std::set<android_color_mode_t> getColorModes() const;
-
-                    // splitLine divides the output into two lines suitable for
-                    // dumpsys SurfaceFlinger
-                    std::string toString(bool splitLine = false) const;
-
-                private:
-                    Display& mDisplay;
-                    hwc2_config_t mId;
-                    std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
-
-                    // Maps from color transform to HWC1 config ID
-                    std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
-            };
-
-            class Changes {
-                public:
-                    uint32_t getNumTypes() const {
-                        return static_cast<uint32_t>(mTypeChanges.size());
-                    }
-
-                    uint32_t getNumLayerRequests() const {
-                        return static_cast<uint32_t>(mLayerRequests.size());
-                    }
-
-                    const std::unordered_map<hwc2_layer_t, HWC2::Composition>&
-                            getTypeChanges() const {
-                        return mTypeChanges;
-                    }
-
-                    const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>&
-                            getLayerRequests() const {
-                        return mLayerRequests;
-                    }
-
-                    int32_t getDisplayRequests() const {
-                        int32_t requests = 0;
-                        for (auto request : mDisplayRequests) {
-                            requests |= static_cast<int32_t>(request);
-                        }
-                        return requests;
-                    }
-
-                    void addTypeChange(hwc2_layer_t layerId,
-                            HWC2::Composition type) {
-                        mTypeChanges.insert({layerId, type});
-                    }
-
-                    void clearTypeChanges() { mTypeChanges.clear(); }
-
-                    void addLayerRequest(hwc2_layer_t layerId,
-                            HWC2::LayerRequest request) {
-                        mLayerRequests.insert({layerId, request});
-                    }
-
-                private:
-                    std::unordered_map<hwc2_layer_t, HWC2::Composition>
-                            mTypeChanges;
-                    std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
-                            mLayerRequests;
-                    std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
-            };
-
-            std::shared_ptr<const Config>
-                    getConfig(hwc2_config_t configId) const;
-
-            void populateColorModes();
-            void initializeActiveConfig();
-
-            void reallocateHwc1Contents();
-            void assignHwc1LayerIds();
-
-            void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
-                    const Layer& layer);
-            void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
-                    const Layer& layer);
-
-            void prepareFramebufferTarget();
-
-            static std::atomic<hwc2_display_t> sNextId;
-            const hwc2_display_t mId;
-            HWC2On1Adapter& mDevice;
-
-            std::atomic<size_t> mDirtyCount;
-
-            // The state of this display should only be modified from
-            // SurfaceFlinger's main loop, with the exception of when dump is
-            // called. To prevent a bad state from crashing us during a dump
-            // call, all public calls into Display must acquire this mutex.
-            //
-            // It is recursive because we don't want to deadlock in validate
-            // (or present) when we call HWC2On1Adapter::prepareAllDisplays
-            // (or setAllDisplays), which calls back into Display functions
-            // which require locking.
-            mutable std::recursive_mutex mStateMutex;
-
-            bool mZIsDirty;
-            HWC1Contents mHwc1RequestedContents;
-            HWC1Contents mHwc1ReceivedContents;
-            DeferredFence mRetireFence;
-
-            // Will only be non-null after the layer has been validated but
-            // before it has been presented
-            std::unique_ptr<Changes> mChanges;
-
-            int32_t mHwc1Id;
-
-            std::vector<std::shared_ptr<Config>> mConfigs;
-            std::shared_ptr<const Config> mActiveConfig;
-            std::set<android_color_mode_t> mColorModes;
-            android_color_mode_t mActiveColorMode;
-            std::string mName;
-            HWC2::DisplayType mType;
-            HWC2::PowerMode mPowerMode;
-            HWC2::Vsync mVsyncEnabled;
-
-            FencedBuffer mClientTarget;
-            FencedBuffer mOutputBuffer;
-
-            bool mHasColorTransform;
-
-            std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
-            std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
-    };
-
-    template <typename ...Args>
-    static int32_t callDisplayFunction(hwc2_device_t* device,
-            hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
-            Args... args) {
-        auto display = getAdapter(device)->getDisplay(displayId);
-        if (!display) {
-            return static_cast<int32_t>(HWC2::Error::BadDisplay);
-        }
-        auto error = ((*display).*member)(std::forward<Args>(args)...);
-        return static_cast<int32_t>(error);
-    }
-
-    template <typename MF, MF memFunc, typename ...Args>
-    static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId,
-            Args... args) {
-        return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc,
-                std::forward<Args>(args)...);
-    }
-
-    static int32_t getDisplayAttributeHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_config_t config,
-            int32_t intAttribute, int32_t* outValue) {
-        auto attribute = static_cast<HWC2::Attribute>(intAttribute);
-        return callDisplayFunction(device, display, &Display::getAttribute,
-                config, attribute, outValue);
-    }
-
-    static int32_t setColorTransformHook(hwc2_device_t* device,
-            hwc2_display_t display, const float* /*matrix*/,
-            int32_t /*android_color_transform_t*/ intHint) {
-        // We intentionally throw away the matrix, because if the hint is
-        // anything other than IDENTITY, we have to fall back to client
-        // composition anyway
-        auto hint = static_cast<android_color_transform_t>(intHint);
-        return callDisplayFunction(device, display, &Display::setColorTransform,
-                hint);
-    }
-
-    static int32_t setColorModeHook(hwc2_device_t* device,
-            hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
-        auto mode = static_cast<android_color_mode_t>(intMode);
-        return callDisplayFunction(device, display, &Display::setColorMode, mode);
-    }
-
-    static int32_t setPowerModeHook(hwc2_device_t* device,
-            hwc2_display_t display, int32_t intMode) {
-        auto mode = static_cast<HWC2::PowerMode>(intMode);
-        return callDisplayFunction(device, display, &Display::setPowerMode,
-                mode);
-    }
-
-    static int32_t setVsyncEnabledHook(hwc2_device_t* device,
-            hwc2_display_t display, int32_t intEnabled) {
-        auto enabled = static_cast<HWC2::Vsync>(intEnabled);
-        return callDisplayFunction(device, display, &Display::setVsyncEnabled,
-                enabled);
-    }
-
-    // Layer functions
-
-    template <typename T>
-    class LatchedState {
-        public:
-            LatchedState(Layer& parent, T initialValue)
-              : mParent(parent),
-                mPendingValue(initialValue),
-                mValue(initialValue) {}
-
-            void setPending(T value) {
-                if (value == mPendingValue) {
-                    return;
-                }
-                if (mPendingValue == mValue) {
-                    mParent.incDirty();
-                } else if (value == mValue) {
-                    mParent.decDirty();
-                }
-                mPendingValue = value;
-            }
-
-            T getValue() const { return mValue; }
-            T getPendingValue() const { return mPendingValue; }
-
-            bool isDirty() const { return mPendingValue != mValue; }
-
-            void latch() {
-                if (isDirty()) {
-                    mValue = mPendingValue;
-                    mParent.decDirty();
-                }
-            }
-
-        private:
-            Layer& mParent;
-            T mPendingValue;
-            T mValue;
-    };
-
-    class Layer {
-        public:
-            Layer(Display& display);
-
-            bool operator==(const Layer& other) { return mId == other.mId; }
-            bool operator!=(const Layer& other) { return !(*this == other); }
-
-            hwc2_layer_t getId() const { return mId; }
-            Display& getDisplay() const { return mDisplay; }
-
-            void incDirty() { if (mDirtyCount++ == 0) mDisplay.incDirty(); }
-            void decDirty() { if (--mDirtyCount == 0) mDisplay.decDirty(); }
-            bool isDirty() const { return mDirtyCount > 0; }
-
-            // HWC2 Layer functions
-            HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
-            HWC2::Error setCursorPosition(int32_t x, int32_t y);
-            HWC2::Error setSurfaceDamage(hwc_region_t damage);
-
-            // HWC2 Layer state functions
-            HWC2::Error setBlendMode(HWC2::BlendMode mode);
-            HWC2::Error setColor(hwc_color_t color);
-            HWC2::Error setCompositionType(HWC2::Composition type);
-            HWC2::Error setDataspace(android_dataspace_t dataspace);
-            HWC2::Error setDisplayFrame(hwc_rect_t frame);
-            HWC2::Error setPlaneAlpha(float alpha);
-            HWC2::Error setSidebandStream(const native_handle_t* stream);
-            HWC2::Error setSourceCrop(hwc_frect_t crop);
-            HWC2::Error setTransform(HWC2::Transform transform);
-            HWC2::Error setVisibleRegion(hwc_region_t visible);
-            HWC2::Error setZ(uint32_t z);
-
-            HWC2::Composition getCompositionType() const {
-                return mCompositionType.getValue();
-            }
-            uint32_t getZ() const { return mZ; }
-
-            void addReleaseFence(int fenceFd);
-            const sp<Fence>& getReleaseFence() const;
-
-            void setHwc1Id(size_t id) { mHwc1Id = id; }
-            size_t getHwc1Id() const { return mHwc1Id; }
-
-            void applyState(struct hwc_layer_1& hwc1Layer, bool applyAllState);
-
-            std::string dump() const;
-
-        private:
-            void applyCommonState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-            void applySolidColorState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-            void applySidebandState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-            void applyBufferState(struct hwc_layer_1& hwc1Layer);
-            void applyCompositionType(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-
-            static std::atomic<hwc2_layer_t> sNextId;
-            const hwc2_layer_t mId;
-            Display& mDisplay;
-            size_t mDirtyCount;
-
-            FencedBuffer mBuffer;
-            std::vector<hwc_rect_t> mSurfaceDamage;
-
-            LatchedState<HWC2::BlendMode> mBlendMode;
-            LatchedState<hwc_color_t> mColor;
-            LatchedState<HWC2::Composition> mCompositionType;
-            LatchedState<hwc_rect_t> mDisplayFrame;
-            LatchedState<float> mPlaneAlpha;
-            LatchedState<const native_handle_t*> mSidebandStream;
-            LatchedState<hwc_frect_t> mSourceCrop;
-            LatchedState<HWC2::Transform> mTransform;
-            LatchedState<std::vector<hwc_rect_t>> mVisibleRegion;
-            uint32_t mZ;
-
-            DeferredFence mReleaseFence;
-
-            size_t mHwc1Id;
-            bool mHasUnsupportedDataspace;
-            bool mHasUnsupportedPlaneAlpha;
-    };
-
-    template <typename ...Args>
-    static int32_t callLayerFunction(hwc2_device_t* device,
-            hwc2_display_t displayId, hwc2_layer_t layerId,
-            HWC2::Error (Layer::*member)(Args...), Args... args) {
-        auto result = getAdapter(device)->getLayer(displayId, layerId);
-        auto error = std::get<HWC2::Error>(result);
-        if (error == HWC2::Error::None) {
-            auto layer = std::get<Layer*>(result);
-            error = ((*layer).*member)(std::forward<Args>(args)...);
-        }
-        return static_cast<int32_t>(error);
-    }
-
-    template <typename MF, MF memFunc, typename ...Args>
-    static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId,
-            hwc2_layer_t layerId, Args... args) {
-        return HWC2On1Adapter::callLayerFunction(device, displayId, layerId,
-                memFunc, std::forward<Args>(args)...);
-    }
-
-    // Layer state functions
-
-    static int32_t setLayerBlendModeHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) {
-        auto mode = static_cast<HWC2::BlendMode>(intMode);
-        return callLayerFunction(device, display, layer,
-                &Layer::setBlendMode, mode);
-    }
-
-    static int32_t setLayerCompositionTypeHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_layer_t layer, int32_t intType) {
-        auto type = static_cast<HWC2::Composition>(intType);
-        return callLayerFunction(device, display, layer,
-                &Layer::setCompositionType, type);
-    }
-
-    static int32_t setLayerDataspaceHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) {
-        auto dataspace = static_cast<android_dataspace_t>(intDataspace);
-        return callLayerFunction(device, display, layer, &Layer::setDataspace,
-                dataspace);
-    }
-
-    static int32_t setLayerTransformHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
-        auto transform = static_cast<HWC2::Transform>(intTransform);
-        return callLayerFunction(device, display, layer, &Layer::setTransform,
-                transform);
-    }
-
-    static int32_t setLayerZOrderHook(hwc2_device_t* device,
-            hwc2_display_t display, hwc2_layer_t layer, uint32_t z) {
-        return callDisplayFunction(device, display, &Display::updateLayerZ,
-                layer, z);
-    }
-
-    // Adapter internals
-
-    void populateCapabilities();
-    Display* getDisplay(hwc2_display_t id);
-    std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId,
-            hwc2_layer_t layerId);
-    void populatePrimary();
-
-    bool prepareAllDisplays();
-    std::vector<struct hwc_display_contents_1*> mHwc1Contents;
-    HWC2::Error setAllDisplays();
-
-    void hwc1Invalidate();
-    void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
-    void hwc1Hotplug(int hwc1DisplayId, int connected);
-
-    // These are set in the constructor and before any asynchronous events are
-    // possible
-
-    struct hwc_composer_device_1* const mHwc1Device;
-    const uint8_t mHwc1MinorVersion;
-    bool mHwc1SupportsVirtualDisplays;
-
-    class Callbacks;
-    const std::unique_ptr<Callbacks> mHwc1Callbacks;
-
-    std::unordered_set<HWC2::Capability> mCapabilities;
-
-    // These are only accessed from the main SurfaceFlinger thread (not from
-    // callbacks or dump
-
-    std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
-    std::shared_ptr<Display> mHwc1VirtualDisplay;
-
-    // These are potentially accessed from multiple threads, and are protected
-    // by this mutex. This needs to be recursive, since the HWC1 implementation
-    // can call back into the invalidate callback on the same thread that is
-    // calling prepare.
-    std::recursive_timed_mutex mStateMutex;
-
-    struct CallbackInfo {
-        hwc2_callback_data_t data;
-        hwc2_function_pointer_t pointer;
-    };
-    std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
-    bool mHasPendingInvalidate;
-    std::vector<std::pair<int, int64_t>> mPendingVsyncs;
-    std::vector<std::pair<int, int>> mPendingHotplugs;
-
-    std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
-    std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
-};
-
-} // namespace android
-
-#endif
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c87ba72..ac2dde2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -43,12 +43,12 @@
 
 #include <android/configuration.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include "HWComposer.h"
-#include "HWC2On1Adapter.h"
 #include "HWC2.h"
+#include "ComposerHal.h"
 
 #include "../Layer.h"           // needed only for debugging
 #include "../SurfaceFlinger.h"
@@ -59,10 +59,8 @@
 
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger),
-      mAdapter(),
-      mHwcDevice(),
+HWComposer::HWComposer(bool useVrComposer)
+    : mHwcDevice(),
       mDisplayData(2),
       mFreeDisplaySlots(),
       mHwcDisplaySlots(),
@@ -76,7 +74,7 @@
         mVSyncCounts[i] = 0;
     }
 
-    loadHwcModule();
+    loadHwcModule(useVrComposer);
 }
 
 HWComposer::~HWComposer() {}
@@ -105,42 +103,10 @@
 }
 
 // Load and prepare the hardware composer module.  Sets mHwc.
-void HWComposer::loadHwcModule()
+void HWComposer::loadHwcModule(bool useVrComposer)
 {
     ALOGV("loadHwcModule");
-
-    hw_module_t const* module;
-
-    if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
-        ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID);
-        abort();
-    }
-
-    hw_device_t* device = nullptr;
-    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
-    if (error != 0) {
-        ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));
-        abort();
-    }
-
-    uint32_t majorVersion = (device->version >> 24) & 0xF;
-    if (majorVersion == 2) {
-        mHwcDevice = std::make_unique<HWC2::Device>(
-                reinterpret_cast<hwc2_device_t*>(device));
-    } else {
-        mAdapter = std::make_unique<HWC2On1Adapter>(
-                reinterpret_cast<hwc_composer_device_1_t*>(device));
-        uint8_t minorVersion = mAdapter->getHwc1MinorVersion();
-        if (minorVersion < 1) {
-            ALOGE("Cannot adapt to HWC version %d.%d",
-                    static_cast<int32_t>((minorVersion >> 8) & 0xF),
-                    static_cast<int32_t>(minorVersion & 0xF));
-            abort();
-        }
-        mHwcDevice = std::make_unique<HWC2::Device>(
-                static_cast<hwc2_device_t*>(mAdapter.get()));
-    }
-
+    mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
 
@@ -199,12 +165,12 @@
         }
         disp = DisplayDevice::DISPLAY_EXTERNAL;
     }
-    mEventHandler->onHotplugReceived(disp,
+    mEventHandler->onHotplugReceived(this, disp,
             connected == HWC2::Connection::Connected);
 }
 
 void HWComposer::invalidate(const std::shared_ptr<HWC2::Display>& /*display*/) {
-    mFlinger->repaintEverything();
+    mEventHandler->onInvalidateReceived(this);
 }
 
 void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display,
@@ -250,7 +216,7 @@
     snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
     ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
-    mEventHandler->onVSyncReceived(disp, timestamp);
+    mEventHandler->onVSyncReceived(this, disp, timestamp);
 }
 
 status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -260,6 +226,15 @@
         return NO_MEMORY;
     }
 
+    if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
+        (width > SurfaceFlinger::maxVirtualDisplaySize ||
+         height > SurfaceFlinger::maxVirtualDisplaySize)) {
+        ALOGE("createVirtualDisplay: Can't create a virtual display with"
+                      " a dimension > %" PRIu64 " (tried %u x %u)",
+              SurfaceFlinger::maxVirtualDisplaySize, width, height);
+        return INVALID_OPERATION;
+    }
+
     std::shared_ptr<HWC2::Display> display;
     auto error = mHwcDevice->createVirtualDisplay(width, height, format,
             &display);
@@ -305,22 +280,22 @@
     return layer;
 }
 
-nsecs_t HWComposer::getRefreshTimestamp(int32_t disp) const {
+nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const {
     // this returns the last refresh timestamp.
     // if the last one is not available, we estimate it based on
     // the refresh period and whatever closest timestamp we have.
     Mutex::Autolock _l(mLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    auto vsyncPeriod = getActiveConfig(disp)->getVsyncPeriod();
-    return now - ((now - mLastHwVSync[disp]) % vsyncPeriod);
+    auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
+    return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
 }
 
-bool HWComposer::isConnected(int32_t disp) const {
-    if (!isValidDisplay(disp)) {
-        ALOGE("isConnected: Attempted to access invalid display %d", disp);
+bool HWComposer::isConnected(int32_t displayId) const {
+    if (!isValidDisplay(displayId)) {
+        ALOGE("isConnected: Attempted to access invalid display %d", displayId);
         return false;
     }
-    return mDisplayData[disp].hwcDisplay->isConnected();
+    return mDisplayData[displayId].hwcDisplay->isConnected();
 }
 
 std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -342,14 +317,14 @@
 std::shared_ptr<const HWC2::Display::Config>
         HWComposer::getActiveConfig(int32_t displayId) const {
     if (!isValidDisplay(displayId)) {
-        ALOGE("getActiveConfigs: Attempted to access invalid display %d",
+        ALOGV("getActiveConfigs: Attempted to access invalid display %d",
                 displayId);
         return nullptr;
     }
     std::shared_ptr<const HWC2::Display::Config> config;
     auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config);
     if (error == HWC2::Error::BadConfig) {
-        ALOGV("getActiveConfig: No config active, returning null");
+        ALOGE("getActiveConfig: No config active, returning null");
         return nullptr;
     } else if (error != HWC2::Error::None) {
         ALOGE("getActiveConfig failed for display %d: %s (%d)", displayId,
@@ -404,14 +379,15 @@
 }
 
 
-void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) {
-    if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) {
-        ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp);
+void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
+    if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
+        ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
         return;
     }
 
-    if (!isValidDisplay(disp)) {
-        ALOGE("setVsyncEnabled: Attempted to access invalid display %d", disp);
+    if (!isValidDisplay(displayId)) {
+        ALOGE("setVsyncEnabled: Attempted to access invalid display %d",
+               displayId);
         return;
     }
 
@@ -420,7 +396,7 @@
     // that even if HWC blocks (which it shouldn't), it won't
     // affect other threads.
     Mutex::Autolock _l(mVsyncLock);
-    auto& displayData = mDisplayData[disp];
+    auto& displayData = mDisplayData[displayId];
     if (enabled != displayData.vsyncEnabled) {
         ATRACE_CALL();
         auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
@@ -428,18 +404,18 @@
             displayData.vsyncEnabled = enabled;
 
             char tag[16];
-            snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp);
+            snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
             ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
         } else {
             ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64
-                    ": %s (%d)", to_string(enabled).c_str(), disp,
-                    mDisplayData[disp].hwcDisplay->getId(),
+                    ": %s (%d)", to_string(enabled).c_str(), displayId,
+                    mDisplayData[displayId].hwcDisplay->getId(),
                     to_string(error).c_str(), static_cast<int32_t>(error));
         }
     }
 }
 
-status_t HWComposer::setClientTarget(int32_t displayId,
+status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot,
         const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
         android_dataspace_t dataspace) {
     if (!isValidDisplay(displayId)) {
@@ -448,11 +424,7 @@
 
     ALOGV("setClientTarget for display %d", displayId);
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
-    buffer_handle_t handle = nullptr;
-    if ((target != nullptr) && target->getNativeBuffer()) {
-        handle = target->getNativeBuffer()->handle;
-    }
-    auto error = hwcDisplay->setClientTarget(handle, acquireFence, dataspace);
+    auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace);
     if (error != HWC2::Error::None) {
         ALOGE("Failed to set client target for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -483,7 +455,34 @@
 
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
-    auto error = hwcDisplay->validate(&numTypes, &numRequests);
+
+    HWC2::Error error = HWC2::Error::None;
+
+    // First try to skip validate altogether if the HWC supports it.
+    displayData.validateWasSkipped = false;
+    if (hasCapability(HWC2::Capability::SkipValidate) &&
+            !displayData.hasClientComposition) {
+        sp<android::Fence> outPresentFence;
+        uint32_t state = UINT32_MAX;
+        error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
+        if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
+            ALOGV("skipValidate: Failed to Present or Validate");
+            return UNKNOWN_ERROR;
+        }
+        if (state == 1) { //Present Succeeded.
+            std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
+            error = hwcDisplay->getReleaseFences(&releaseFences);
+            displayData.releaseFences = std::move(releaseFences);
+            displayData.lastPresentFence = outPresentFence;
+            displayData.validateWasSkipped = true;
+            displayData.presentError = error;
+            return NO_ERROR;
+        }
+        // Present failed but Validate ran.
+    } else {
+        error = hwcDisplay->validate(&numTypes, &numRequests);
+    }
+    ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
     if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
         ALOGE("prepare: validate failed for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -589,12 +588,12 @@
     return mDisplayData[displayId].hasClientComposition;
 }
 
-sp<Fence> HWComposer::getRetireFence(int32_t displayId) const {
+sp<Fence> HWComposer::getPresentFence(int32_t displayId) const {
     if (!isValidDisplay(displayId)) {
-        ALOGE("getRetireFence failed for invalid display %d", displayId);
+        ALOGE("getPresentFence failed for invalid display %d", displayId);
         return Fence::NO_FENCE;
     }
-    return mDisplayData[displayId].lastRetireFence;
+    return mDisplayData[displayId].lastPresentFence;
 }
 
 sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
@@ -611,7 +610,7 @@
     return displayFences[layer];
 }
 
-status_t HWComposer::commit(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
     ATRACE_CALL();
 
     if (!isValidDisplay(displayId)) {
@@ -620,17 +619,30 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
-    auto error = hwcDisplay->present(&displayData.lastRetireFence);
+
+    if (displayData.validateWasSkipped) {
+        hwcDisplay->discardCommands();
+        auto error = displayData.presentError;
+        if (error != HWC2::Error::None) {
+            ALOGE("skipValidate: failed for display %d: %s (%d)",
+                  displayId, to_string(error).c_str(), static_cast<int32_t>(error));
+            return UNKNOWN_ERROR;
+        }
+        return NO_ERROR;
+    }
+
+    auto error = hwcDisplay->present(&displayData.lastPresentFence);
     if (error != HWC2::Error::None) {
-        ALOGE("commit: present failed for display %d: %s (%d)", displayId,
-                to_string(error).c_str(), static_cast<int32_t>(error));
+        ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
+              displayId, to_string(error).c_str(), static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
     }
 
     std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
     error = hwcDisplay->getReleaseFences(&releaseFences);
     if (error != HWC2::Error::None) {
-        ALOGE("commit: Failed to get release fences for display %d: %s (%d)",
+        ALOGE("presentAndGetReleaseFences: Failed to get release fences "
+              "for display %d: %s (%d)",
                 displayId, to_string(error).c_str(),
                 static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
@@ -856,6 +868,10 @@
 }
 */
 
+bool HWComposer::isUsingVrComposer() const {
+    return getComposer()->isUsingVrComposer();
+}
+
 void HWComposer::dump(String8& result) const {
     // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
     // all the state going into the layers. This is probably better done in
@@ -869,7 +885,7 @@
   : hasClientComposition(false),
     hasDeviceComposition(false),
     hwcDisplay(),
-    lastRetireFence(Fence::NO_FENCE),
+    lastPresentFence(Fence::NO_FENCE),
     outbufHandle(nullptr),
     outbufAcquireFence(Fence::NO_FENCE),
     vsyncEnabled(HWC2::Vsync::Disable) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41671f6..7463362 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -58,24 +58,27 @@
 class Fence;
 class FloatRect;
 class GraphicBuffer;
-class HWC2On1Adapter;
 class NativeHandle;
 class Region;
 class String8;
-class SurfaceFlinger;
 
 class HWComposer
 {
 public:
     class EventHandler {
         friend class HWComposer;
-        virtual void onVSyncReceived(int32_t disp, nsecs_t timestamp) = 0;
-        virtual void onHotplugReceived(int32_t disp, bool connected) = 0;
+        virtual void onVSyncReceived(
+            HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
+        virtual void onHotplugReceived(HWComposer* composer, int32_t disp, bool connected) = 0;
+        virtual void onInvalidateReceived(HWComposer* composer) = 0;
     protected:
         virtual ~EventHandler() {}
     };
 
-    HWComposer(const sp<SurfaceFlinger>& flinger);
+    // useVrComposer is passed to the composer HAL. When true, the composer HAL
+    // will use the vr composer service, otherwise it uses the real hardware
+    // composer.
+    HWComposer(bool useVrComposer);
 
     ~HWComposer();
 
@@ -94,11 +97,12 @@
     // Asks the HAL what it can do
     status_t prepare(DisplayDevice& displayDevice);
 
-    status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence,
+    status_t setClientTarget(int32_t displayId, uint32_t slot,
+            const sp<Fence>& acquireFence,
             const sp<GraphicBuffer>& target, android_dataspace_t dataspace);
 
-    // Finalize the layers and present them
-    status_t commit(int32_t displayId);
+    // Present layers to the display and read releaseFences.
+    status_t presentAndGetReleaseFences(int32_t displayId);
 
     // set power mode
     status_t setPowerMode(int32_t displayId, int mode);
@@ -118,9 +122,8 @@
     // does this display have layers handled by GLES
     bool hasClientComposition(int32_t displayId) const;
 
-    // get the retire fence for the previous frame (i.e., corresponding to the
-    // last call to presentDisplay
-    sp<Fence> getRetireFence(int32_t displayId) const;
+    // get the present fence received from the last call to present.
+    sp<Fence> getPresentFence(int32_t displayId) const;
 
     // Get last release fence for the given layer
     sp<Fence> getLayerReleaseFence(int32_t displayId,
@@ -140,12 +143,12 @@
 
     // Events handling ---------------------------------------------------------
 
-    void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
+    void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
 
     // Query display parameters.  Pass in a display index (e.g.
     // HWC_DISPLAY_PRIMARY).
-    nsecs_t getRefreshTimestamp(int32_t disp) const;
-    bool isConnected(int32_t disp) const;
+    nsecs_t getRefreshTimestamp(int32_t displayId) const;
+    bool isConnected(int32_t displayId) const;
 
     // Non-const because it can update configMap inside of mDisplayData
     std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -158,13 +161,16 @@
 
     status_t setActiveColorMode(int32_t displayId, android_color_mode_t mode);
 
+    bool isUsingVrComposer() const;
+
     // for debugging ----------------------------------------------------------
     void dump(String8& out) const;
 
+    android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
 private:
     static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2;
 
-    void loadHwcModule();
+    void loadHwcModule(bool useVrComposer);
 
     bool isValidDisplay(int32_t displayId) const;
     static void validateChange(HWC2::Composition from, HWC2::Composition to);
@@ -186,7 +192,7 @@
         bool hasDeviceComposition;
         std::shared_ptr<HWC2::Display> hwcDisplay;
         HWC2::DisplayRequest displayRequests;
-        sp<Fence> lastRetireFence;  // signals when the last set op retires
+        sp<Fence> lastPresentFence;  // signals when the last set op retires
         std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>>
                 releaseFences;
         buffer_handle_t outbufHandle;
@@ -196,10 +202,11 @@
 
         // protected by mVsyncLock
         HWC2::Vsync vsyncEnabled;
+
+        bool validateWasSkipped;
+        HWC2::Error presentError;
     };
 
-    sp<SurfaceFlinger>              mFlinger;
-    std::unique_ptr<HWC2On1Adapter> mAdapter;
     std::unique_ptr<HWC2::Device>   mHwcDevice;
     std::vector<DisplayData>        mDisplayData;
     std::set<size_t>                mFreeDisplaySlots;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
new file mode 100644
index 0000000..a234b63
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HWComposerBufferCache.h"
+
+#include <gui/BufferQueue.h>
+
+namespace android {
+
+HWComposerBufferCache::HWComposerBufferCache()
+{
+    mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
+}
+
+void HWComposerBufferCache::getHwcBuffer(int slot,
+        const sp<GraphicBuffer>& buffer,
+        uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
+{
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
+        // default to slot 0
+        slot = 0;
+    }
+
+    if (static_cast<size_t>(slot) >= mBuffers.size()) {
+        mBuffers.resize(slot + 1);
+    }
+
+    *outSlot = slot;
+
+    if (mBuffers[slot] == buffer) {
+        // already cached in HWC, skip sending the buffer
+        *outBuffer = nullptr;
+    } else {
+        *outBuffer = buffer;
+
+        // update cache
+        mBuffers[slot] = buffer;
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
new file mode 100644
index 0000000..a008ca9
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+#define ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+
+#include <stdint.h>
+
+#include <utils/StrongPointer.h>
+
+#include <vector>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GraphicBuffer;
+
+// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
+// HWC display and layer.  When updating a display target or a layer buffer,
+// we have the option to send the buffer handle over or to request the HAL to
+// retrieve it from its cache.  The latter is cheaper since it eliminates the
+// overhead to transfer the handle over the trasport layer, and the overhead
+// for the HAL to clone and retain the handle.
+//
+// To be able to find out whether a buffer is already in the HAL's cache, we
+// use HWComposerBufferCache to mirror the cache in SF.
+class HWComposerBufferCache {
+public:
+    HWComposerBufferCache();
+
+    // Given a buffer queue slot and buffer, return the HWC cache slot and
+    // buffer to be sent to HWC.
+    //
+    // outBuffer is set to buffer when buffer is not in the HWC cache;
+    // otherwise, outBuffer is set to nullptr.
+    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
+            uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+
+private:
+    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
+    // less than BufferQueue::NUM_BUFFER_SLOTS).
+    std::vector<sp<GraphicBuffer>> mBuffers;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index ef41658..dcb2913 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -39,8 +39,8 @@
 
 #include <android/configuration.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <system/graphics.h>
 
@@ -277,7 +277,7 @@
 }
 
 void HWComposer::invalidate() {
-    mFlinger->repaintEverything();
+    mEventHandler.onInvalidateReceived(this);
 }
 
 void HWComposer::vsync(int disp, int64_t timestamp) {
@@ -302,7 +302,7 @@
         snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
         ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
-        mEventHandler.onVSyncReceived(disp, timestamp);
+        mEventHandler.onVSyncReceived(this, disp, timestamp);
     }
 }
 
@@ -315,7 +315,7 @@
     queryDisplayProperties(disp);
     // Do not teardown or recreate the primary display
     if (disp != HWC_DISPLAY_PRIMARY) {
-        mEventHandler.onHotplugReceived(disp, bool(connected));
+        mEventHandler.onHotplugReceived(this, disp, bool(connected));
     }
 }
 
@@ -933,7 +933,7 @@
 protected:
     HWCTYPE* const mLayerList;
     HWCTYPE* mCurrentLayer;
-    Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+    explicit Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
             mIndex(0) { }
     inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
     inline HWCTYPE* getLayer() { return mCurrentLayer; }
@@ -1159,6 +1159,8 @@
     switch (format) {
     case PIXEL_FORMAT_RGBA_8888:    return String8("RGBA_8888");
     case PIXEL_FORMAT_RGBX_8888:    return String8("RGBx_8888");
+    case PIXEL_FORMAT_RGBA_FP16:    return String8("RGBA_FP16");
+    case PIXEL_FORMAT_RGBA_1010102: return String8("RGBA_1010102");
     case PIXEL_FORMAT_RGB_888:      return String8("RGB_888");
     case PIXEL_FORMAT_RGB_565:      return String8("RGB_565");
     case PIXEL_FORMAT_BGRA_8888:    return String8("BGRA_8888");
@@ -1317,7 +1319,7 @@
     } while (err<0 && errno == EINTR);
 
     if (err == 0) {
-        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
+        mHwc.mEventHandler.onVSyncReceived(&mHwc, 0, next_vsync);
     }
 
     return true;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index 170e382..4bc63bb 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -60,8 +60,10 @@
 public:
     class EventHandler {
         friend class HWComposer;
-        virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0;
-        virtual void onHotplugReceived(int disp, bool connected) = 0;
+        virtual void onVSyncReceived(
+            HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
+        virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected) = 0;
+        virtual void onInvalidateReceived(HWComposer* composer) = 0;
     protected:
         virtual ~EventHandler() {}
     };
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
index bd50b4a..a6f076e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
+#include <android/hardware/power/1.0/IPower.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <cutils/log.h>
+#include <android/log.h>
 #include <utils/Errors.h>
 
 #include <binder/IServiceManager.h>
@@ -26,6 +27,7 @@
 
 #include "PowerHAL.h"
 
+using android::hardware::power::V1_0::PowerHint;
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -39,7 +41,9 @@
         }
         mPowerManager = interface_cast<IPowerManager>(bs);
     }
-    status_t status = mPowerManager->powerHint(POWER_HINT_VSYNC, enabled ? 1 : 0);
+    status_t status;
+    status = mPowerManager->powerHint(static_cast<int>(PowerHint::VSYNC),
+            enabled ? 1 : 0);
     if(status == DEAD_OBJECT) {
         mPowerManager = NULL;
     }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..8217540 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -17,20 +17,16 @@
 // #define LOG_NDEBUG 0
 #include "VirtualDisplaySurface.h"
 #include "HWComposer.h"
+#include "SurfaceFlinger.h"
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/IProducerListener.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
 
-#if defined(FORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS)
-static const bool sForceHwcCopy = true;
-#else
-static const bool sForceHwcCopy = false;
-#endif
-
 #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
         mDisplayName.string(), ##__VA_ARGS__)
 #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
@@ -73,7 +69,8 @@
     mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
     mDbgState(DBG_STATE_IDLE),
     mDbgLastCompositionType(COMPOSITION_UNKNOWN),
-    mMustRecompose(false)
+    mMustRecompose(false),
+    mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv)
 {
     mSource[SOURCE_SINK] = sink;
     mSource[SOURCE_SCRATCH] = bqProducer;
@@ -136,7 +133,7 @@
     mDbgState = DBG_STATE_PREPARED;
 
     mCompositionType = compositionType;
-    if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+    if (mForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
         // Some hardware can do RGB->YUV conversion more efficiently in hardware
         // controlled by HWC than in hardware controlled by the video encoder.
         // Forcing GLES-composed frames to go through an extra copy by the HWC
@@ -221,9 +218,14 @@
     status_t result = NO_ERROR;
     if (fbBuffer != NULL) {
 #ifdef USE_HWC2
+        uint32_t hwcSlot = 0;
+        sp<GraphicBuffer> hwcBuffer;
+        mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
+                &hwcSlot, &hwcBuffer);
+
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(mDisplayId, mFbFence, fbBuffer,
-                HAL_DATASPACE_UNKNOWN);
+        result = mHwc.setClientTarget(mDisplayId, hwcSlot, mFbFence,
+                hwcBuffer, HAL_DATASPACE_UNKNOWN);
 #else
         result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
 #endif
@@ -241,7 +243,7 @@
     mDbgState = DBG_STATE_IDLE;
 
 #ifdef USE_HWC2
-    sp<Fence> retireFence = mHwc.getRetireFence(mDisplayId);
+    sp<Fence> retireFence = mHwc.getPresentFence(mDisplayId);
 #else
     sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
 #endif
@@ -281,7 +283,7 @@
 #endif
                     &qbo);
             if (result == NO_ERROR) {
-                updateQueueBufferOutput(qbo);
+                updateQueueBufferOutput(std::move(qbo));
             }
         } else {
             // If the surface hadn't actually been updated, then we only went
@@ -303,13 +305,8 @@
 }
 
 void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
-    uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
-    uint64_t nextFrameNumber;
-    mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers,
-            &nextFrameNumber);
-    mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers,
-            nextFrameNumber);
-
+    mQueueBufferOutput.width = w;
+    mQueueBufferOutput.height = h;
     mSinkBufferWidth = w;
     mSinkBufferHeight = h;
 }
@@ -345,7 +342,7 @@
     LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
 
     status_t result = mSource[source]->dequeueBuffer(sslot, fence,
-            mSinkBufferWidth, mSinkBufferHeight, format, usage);
+            mSinkBufferWidth, mSinkBufferHeight, format, usage, nullptr);
     if (result < 0)
         return result;
     int pslot = mapSource2ProducerSlot(source, *sslot);
@@ -384,9 +381,12 @@
 }
 
 status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
-    if (mDisplayId < 0)
-        return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage);
+        uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+        FrameEventHistoryDelta* outTimestamps) {
+    if (mDisplayId < 0) {
+        return mSource[SOURCE_SINK]->dequeueBuffer(
+                pslot, fence, w, h, format, usage, outTimestamps);
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
             "Unexpected dequeueBuffer() in %s state", dbgStateStr());
@@ -518,7 +518,8 @@
         mOutputFence = mFbFence;
     }
 
-    *output = mQueueBufferOutput;
+    // This moves the frame timestamps and keeps a copy of all other fields.
+    *output = std::move(mQueueBufferOutput);
     return NO_ERROR;
 }
 
@@ -557,8 +558,9 @@
     status_t result = mSource[SOURCE_SINK]->connect(listener, api,
             producerControlledByApp, &qbo);
     if (result == NO_ERROR) {
-        updateQueueBufferOutput(qbo);
-        *output = mQueueBufferOutput;
+        updateQueueBufferOutput(std::move(qbo));
+        // This moves the frame timestamps and keeps a copy of all other fields.
+        *output = std::move(mQueueBufferOutput);
     }
     return result;
 }
@@ -617,11 +619,9 @@
 }
 
 void VirtualDisplaySurface::updateQueueBufferOutput(
-        const QueueBufferOutput& qbo) {
-    uint32_t w, h, transformHint, numPendingBuffers;
-    uint64_t nextFrameNumber;
-    qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber);
-    mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber);
+        QueueBufferOutput&& qbo) {
+    mQueueBufferOutput = std::move(qbo);
+    mQueueBufferOutput.transformHint = 0;
 }
 
 void VirtualDisplaySurface::resetPerFrameState() {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 70f717f..5c0e084 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -17,11 +17,12 @@
 #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 
+#include "DisplaySurface.h"
+#include "HWComposerBufferCache.h"
+
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
-#include "DisplaySurface.h"
-
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -104,7 +105,8 @@
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual status_t setAsyncMode(bool async);
     virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w,
-            uint32_t h, PixelFormat format, uint32_t usage);
+            uint32_t h, PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta *outTimestamps);
     virtual status_t detachBuffer(int slot);
     virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence);
@@ -135,7 +137,7 @@
     static Source fbSourceForCompositionType(CompositionType type);
     status_t dequeueBuffer(Source source, PixelFormat format, uint32_t usage,
             int* sslot, sp<Fence>* fence);
-    void updateQueueBufferOutput(const QueueBufferOutput& qbo);
+    void updateQueueBufferOutput(QueueBufferOutput&& qbo);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
 
@@ -175,11 +177,13 @@
     // slot. Both mProducerSlotSource and mProducerBuffers are indexed by a
     // "producer slot"; see the mapSlot*() functions.
     uint64_t mProducerSlotSource;
-    sp<GraphicBuffer> mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+    sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // The QueueBufferOutput with the latest info from the sink, and with the
     // transform hint cleared. Since we defer queueBuffer from the GLES driver
     // to the sink, we have to return the previous version.
+    // Moves instead of copies are performed to avoid duplicate
+    // FrameEventHistoryDeltas.
     QueueBufferOutput mQueueBufferOutput;
 
     // Details of the current sink buffer. These become valid when a buffer is
@@ -247,6 +251,13 @@
     static const char* dbgSourceStr(Source s);
 
     bool mMustRecompose;
+
+#ifdef USE_HWC2
+    HWComposerBufferCache mHwcBufferCache;
+#endif
+
+
+    bool mForceHwcCopy;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index a104e8f..c953c68 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "Daltonizer.h"
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index d21b155..2fb60e9 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -17,7 +17,7 @@
 #ifndef SF_EFFECTS_DALTONIZER_H_
 #define SF_EFFECTS_DALTONIZER_H_
 
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index be6c53a..1b1ef75 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -29,7 +29,7 @@
 class EventControlThread: public Thread {
 public:
 
-    EventControlThread(const sp<SurfaceFlinger>& flinger);
+    explicit EventControlThread(const sp<SurfaceFlinger>& flinger);
     virtual ~EventControlThread() {}
 
     void setVsyncEnabled(bool enabled);
@@ -45,4 +45,4 @@
 
 }
 
-#endif // ANDROID_DISPSYNC_H
+#endif // ANDROID_EVENTCONTROLTHREAD_H
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
index 47bab83..365a0bd 100644
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ b/services/surfaceflinger/EventLog/EventLog.cpp
@@ -16,7 +16,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <utils/String8.h>
 
 #include "EventLog.h"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
index 5207514..efc5d70 100644
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ b/services/surfaceflinger/EventLog/EventLog.h
@@ -52,7 +52,7 @@
         bool mOverflow;
         char mStorage[STORAGE_MAX_SIZE];
     public:
-        TagBuffer(int32_t tag);
+        explicit TagBuffer(int32_t tag);
 
         // starts list of items
         void startList(int8_t count);
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index dd88adb..a9bb2ba 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -21,7 +21,6 @@
 
 #include <cutils/compiler.h>
 
-#include <gui/BitTube.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/DisplayEventReceiver.h>
 
@@ -44,13 +43,14 @@
     return;
 }
 
-EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger)
+EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger, bool interceptVSyncs)
     : mVSyncSource(src),
       mFlinger(flinger),
       mUseSoftwareVSync(false),
       mVsyncEnabled(false),
       mDebugVsyncEnabled(false),
-      mVsyncHintSent(false) {
+      mVsyncHintSent(false),
+      mInterceptVSyncs(interceptVSyncs) {
 
     for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
         mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
@@ -226,6 +226,9 @@
             timestamp = mVSyncEvent[i].header.timestamp;
             if (timestamp) {
                 // we have a vsync event to dispatch
+                if (mInterceptVSyncs) {
+                    mFlinger.mInterceptor.saveVSyncEvent(timestamp);
+                }
                 *event = mVSyncEvent[i];
                 mVSyncEvent[i].header.timestamp = 0;
                 vsyncCount = mVSyncEvent[i].vsync.count;
@@ -385,7 +388,7 @@
 
 EventThread::Connection::Connection(
         const sp<EventThread>& eventThread)
-    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
+    : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize)
 {
 }
 
@@ -399,12 +402,14 @@
     mEventThread->registerDisplayEventConnection(this);
 }
 
-sp<BitTube> EventThread::Connection::getDataChannel() const {
-    return mChannel;
+status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) {
+    outChannel->setReceiveFd(mChannel.moveReceiveFd());
+    return NO_ERROR;
 }
 
-void EventThread::Connection::setVsyncRate(uint32_t count) {
+status_t EventThread::Connection::setVsyncRate(uint32_t count) {
     mEventThread->setVsyncRate(count, this);
+    return NO_ERROR;
 }
 
 void EventThread::Connection::requestNextVsync() {
@@ -413,7 +418,7 @@
 
 status_t EventThread::Connection::postEvent(
         const DisplayEventReceiver::Event& event) {
-    ssize_t size = DisplayEventReceiver::sendEvents(mChannel, &event, 1);
+    ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 34654fa..6a59fbb 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <private/gui/BitTube.h>
 #include <gui/DisplayEventReceiver.h>
 #include <gui/IDisplayEventConnection.h>
 
@@ -57,7 +58,7 @@
 class EventThread : public Thread, private VSyncSource::Callback {
     class Connection : public BnDisplayEventConnection {
     public:
-        Connection(const sp<EventThread>& eventThread);
+        explicit Connection(const sp<EventThread>& eventThread);
         status_t postEvent(const DisplayEventReceiver::Event& event);
 
         // count >= 1 : continuous event. count is the vsync rate
@@ -68,16 +69,16 @@
     private:
         virtual ~Connection();
         virtual void onFirstRef();
-        virtual sp<BitTube> getDataChannel() const;
-        virtual void setVsyncRate(uint32_t count);
-        virtual void requestNextVsync();    // asynchronous
+        status_t stealReceiveChannel(gui::BitTube* outChannel) override;
+        status_t setVsyncRate(uint32_t count) override;
+        void requestNextVsync() override;    // asynchronous
         sp<EventThread> const mEventThread;
-        sp<BitTube> const mChannel;
+        gui::BitTube mChannel;
     };
 
 public:
 
-    EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger);
+    EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger, bool interceptVSyncs);
 
     sp<Connection> createEventConnection() const;
     status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -132,6 +133,7 @@
     bool mDebugVsyncEnabled;
 
     bool mVsyncHintSent;
+    const bool mInterceptVSyncs;
     timer_t mTimerId;
 };
 
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
deleted file mode 100644
index 0e18a93..0000000
--- a/services/surfaceflinger/FenceTracker.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <inttypes.h>
-#include "FenceTracker.h"
-#include "Layer.h"
-#include <utils/Trace.h>
-
-namespace android {
-
-FenceTracker::FenceTracker() :
-        mFrameCounter(0),
-        mOffset(0),
-        mFrames(),
-        mMutex() {
-}
-
-void FenceTracker::dump(String8* outString) {
-    Mutex::Autolock lock(mMutex);
-    checkFencesForCompletion();
-
-    for (size_t i = 0; i < MAX_FRAME_HISTORY; i++) {
-        int index = (mOffset + i) % MAX_FRAME_HISTORY;
-        const FrameRecord& frame = mFrames[index];
-
-        outString->appendFormat("Frame %" PRIu64 "\n", frame.frameId);
-        outString->appendFormat("- Refresh start\t%" PRId64 "\n",
-                frame.refreshStartTime);
-
-        if (frame.glesCompositionDoneTime) {
-            outString->appendFormat("- GLES done\t%" PRId64 "\n",
-                    frame.glesCompositionDoneTime);
-        } else if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
-            outString->append("- GLES done\tNot signaled\n");
-        }
-        if (frame.retireTime) {
-            outString->appendFormat("- Retire\t%" PRId64 "\n",
-                    frame.retireTime);
-        } else {
-            outString->append("- Retire\tNot signaled\n");
-        }
-        for (const auto& kv : frame.layers) {
-            const LayerRecord& layer = kv.second;
-            outString->appendFormat("-- %s\n", layer.name.string());
-            outString->appendFormat("---- Frame # %" PRIu64 " (%s)\n",
-                    layer.frameNumber,
-                    layer.isGlesComposition ? "GLES" : "HWC");
-            outString->appendFormat("---- Posted\t%" PRId64 "\n",
-                    layer.postedTime);
-            if (layer.acquireTime) {
-                outString->appendFormat("---- Acquire\t%" PRId64 "\n",
-                        layer.acquireTime);
-            } else {
-                outString->append("---- Acquire\tNot signaled\n");
-            }
-            if (layer.releaseTime) {
-                outString->appendFormat("---- Release\t%" PRId64 "\n",
-                        layer.releaseTime);
-            } else {
-                outString->append("---- Release\tNot signaled\n");
-            }
-        }
-    }
-}
-
-static inline bool isValidTimestamp(nsecs_t time) {
-    return time > 0 && time < INT64_MAX;
-}
-
-void FenceTracker::checkFencesForCompletion() {
-    ATRACE_CALL();
-    for (auto& frame : mFrames) {
-        if (frame.retireFence != Fence::NO_FENCE) {
-            nsecs_t time = frame.retireFence->getSignalTime();
-            if (isValidTimestamp(time)) {
-                frame.retireTime = time;
-                frame.retireFence = Fence::NO_FENCE;
-            }
-        }
-        if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
-            nsecs_t time = frame.glesCompositionDoneFence->getSignalTime();
-            if (isValidTimestamp(time)) {
-                frame.glesCompositionDoneTime = time;
-                frame.glesCompositionDoneFence = Fence::NO_FENCE;
-            }
-        }
-        for (auto& kv : frame.layers) {
-            LayerRecord& layer = kv.second;
-            if (layer.acquireFence != Fence::NO_FENCE) {
-                nsecs_t time = layer.acquireFence->getSignalTime();
-                if (isValidTimestamp(time)) {
-                    layer.acquireTime = time;
-                    layer.acquireFence = Fence::NO_FENCE;
-                }
-            }
-            if (layer.releaseFence != Fence::NO_FENCE) {
-                nsecs_t time = layer.releaseFence->getSignalTime();
-                if (isValidTimestamp(time)) {
-                    layer.releaseTime = time;
-                    layer.releaseFence = Fence::NO_FENCE;
-                }
-            }
-        }
-    }
-}
-
-void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
-        const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence) {
-    ATRACE_CALL();
-    Mutex::Autolock lock(mMutex);
-    FrameRecord& frame = mFrames[mOffset];
-    FrameRecord& prevFrame = mFrames[(mOffset + MAX_FRAME_HISTORY - 1) %
-                                     MAX_FRAME_HISTORY];
-    frame.layers.clear();
-
-    bool wasGlesCompositionDone = false;
-    const size_t count = layers.size();
-    for (size_t i = 0; i < count; i++) {
-        String8 name;
-        uint64_t frameNumber;
-        bool glesComposition;
-        nsecs_t postedTime;
-        sp<Fence> acquireFence;
-        sp<Fence> prevReleaseFence;
-        int32_t layerId = layers[i]->getSequence();
-
-        layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
-                &postedTime, &acquireFence, &prevReleaseFence);
-#ifdef USE_HWC2
-        if (glesComposition) {
-            frame.layers.emplace(std::piecewise_construct,
-                    std::forward_as_tuple(layerId),
-                    std::forward_as_tuple(name, frameNumber, glesComposition,
-                    postedTime, 0, 0, acquireFence, prevReleaseFence));
-            wasGlesCompositionDone = true;
-        } else {
-            frame.layers.emplace(std::piecewise_construct,
-                    std::forward_as_tuple(layerId),
-                    std::forward_as_tuple(name, frameNumber, glesComposition,
-                    postedTime, 0, 0, acquireFence, Fence::NO_FENCE));
-            auto prevLayer = prevFrame.layers.find(layerId);
-            if (prevLayer != prevFrame.layers.end()) {
-                prevLayer->second.releaseFence = prevReleaseFence;
-            }
-        }
-#else
-        frame.layers.emplace(std::piecewise_construct,
-                std::forward_as_tuple(layerId),
-                std::forward_as_tuple(name, frameNumber, glesComposition,
-                postedTime, 0, 0, acquireFence,
-                glesComposition ? Fence::NO_FENCE : prevReleaseFence));
-        if (glesComposition) {
-            wasGlesCompositionDone = true;
-        }
-#endif
-        frame.layers.emplace(std::piecewise_construct,
-                std::forward_as_tuple(layerId),
-                std::forward_as_tuple(name, frameNumber, glesComposition,
-                postedTime, 0, 0, acquireFence, prevReleaseFence));
-    }
-
-    frame.frameId = mFrameCounter;
-    frame.refreshStartTime = refreshStartTime;
-    frame.retireTime = 0;
-    frame.glesCompositionDoneTime = 0;
-    prevFrame.retireFence = retireFence;
-    frame.retireFence = Fence::NO_FENCE;
-    frame.glesCompositionDoneFence = wasGlesCompositionDone ? glDoneFence :
-            Fence::NO_FENCE;
-
-    mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
-    mFrameCounter++;
-}
-
-bool FenceTracker::getFrameTimestamps(const Layer& layer,
-        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
-    Mutex::Autolock lock(mMutex);
-    checkFencesForCompletion();
-    int32_t layerId = layer.getSequence();
-
-    size_t i = 0;
-    for (; i < MAX_FRAME_HISTORY; i++) {
-       if (mFrames[i].layers.count(layerId) &&
-               mFrames[i].layers[layerId].frameNumber == frameNumber) {
-           break;
-       }
-    }
-    if (i == MAX_FRAME_HISTORY) {
-        return false;
-    }
-
-    const FrameRecord& frameRecord = mFrames[i];
-    const LayerRecord& layerRecord = mFrames[i].layers[layerId];
-    outTimestamps->frameNumber = frameNumber;
-    outTimestamps->postedTime = layerRecord.postedTime;
-    outTimestamps->acquireTime = layerRecord.acquireTime;
-    outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
-    outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
-    outTimestamps->displayRetireTime = frameRecord.retireTime;
-    outTimestamps->releaseTime = layerRecord.releaseTime;
-    return true;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
deleted file mode 100644
index 4cb14a5..0000000
--- a/services/surfaceflinger/FenceTracker.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FENCETRACKER_H
-#define ANDROID_FENCETRACKER_H
-
-#include <ui/Fence.h>
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <unordered_map>
-
-namespace android {
-
-class Layer;
-struct FrameTimestamps;
-/*
- * Keeps a circular buffer of fence/timestamp data for the last N frames in
- * SurfaceFlinger. Gets timestamps for fences after they have signaled.
- */
-class FenceTracker {
-public:
-     FenceTracker();
-     void dump(String8* outString);
-     void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
-             const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
-     bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
-             FrameTimestamps* outTimestamps);
-
-protected:
-     static constexpr size_t MAX_FRAME_HISTORY = 8;
-
-     struct LayerRecord {
-         String8 name; // layer name
-         uint64_t frameNumber; // frame number for this layer
-         bool isGlesComposition; // was GLES composition used for this layer?
-         nsecs_t postedTime; // time when buffer was queued
-         nsecs_t acquireTime; // timestamp from the acquire fence
-         nsecs_t releaseTime; // timestamp from the release fence
-         sp<Fence> acquireFence; // acquire fence
-         sp<Fence> releaseFence; // release fence
-
-         LayerRecord(const String8& name, uint64_t frameNumber,
-                 bool isGlesComposition, nsecs_t postedTime,
-                 nsecs_t acquireTime, nsecs_t releaseTime,
-                 sp<Fence> acquireFence, sp<Fence> releaseFence) :
-                 name(name), frameNumber(frameNumber),
-                 isGlesComposition(isGlesComposition), postedTime(postedTime),
-                 acquireTime(acquireTime), releaseTime(releaseTime),
-                 acquireFence(acquireFence), releaseFence(releaseFence) {};
-         LayerRecord() : name("uninitialized"), frameNumber(0),
-                 isGlesComposition(false), postedTime(0), acquireTime(0),
-                 releaseTime(0), acquireFence(Fence::NO_FENCE),
-                 releaseFence(Fence::NO_FENCE) {};
-     };
-
-     struct FrameRecord {
-         // global SurfaceFlinger frame counter
-         uint64_t frameId;
-         // layer data for this frame
-         std::unordered_map<int32_t, LayerRecord> layers;
-         // timestamp for when SurfaceFlinger::handleMessageRefresh() was called
-         nsecs_t refreshStartTime;
-         // timestamp from the retire fence
-         nsecs_t retireTime;
-         // timestamp from the GLES composition completion fence
-         nsecs_t glesCompositionDoneTime;
-         // primary display retire fence for this frame
-         sp<Fence> retireFence;
-         // if GLES composition was done, the fence for its completion
-         sp<Fence> glesCompositionDoneFence;
-
-         FrameRecord() : frameId(0), layers(), refreshStartTime(0),
-                 retireTime(0), glesCompositionDoneTime(0),
-                 retireFence(Fence::NO_FENCE),
-                 glesCompositionDoneFence(Fence::NO_FENCE) {}
-     };
-
-     uint64_t mFrameCounter;
-     uint32_t mOffset;
-     FrameRecord mFrames[MAX_FRAME_HISTORY];
-     Mutex mMutex;
-
-     void checkFencesForCompletion();
-};
-
-}
-
-#endif // ANDROID_FRAMETRACKER_H
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index c09bbe4..99c4daa 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -19,13 +19,11 @@
 
 #include <inttypes.h>
 
-#include <cutils/log.h>
-
-#include <ui/Fence.h>
-#include <ui/FrameStats.h>
-
+#include <android/log.h>
 #include <utils/String8.h>
 
+#include <ui/FrameStats.h>
+
 #include "FrameTracker.h"
 #include "EventLog/EventLog.h"
 
@@ -48,9 +46,10 @@
     mFrameRecords[mOffset].frameReadyTime = readyTime;
 }
 
-void FrameTracker::setFrameReadyFence(const sp<Fence>& readyFence) {
+void FrameTracker::setFrameReadyFence(
+        std::shared_ptr<FenceTime>&& readyFence) {
     Mutex::Autolock lock(mMutex);
-    mFrameRecords[mOffset].frameReadyFence = readyFence;
+    mFrameRecords[mOffset].frameReadyFence = std::move(readyFence);
     mNumFences++;
 }
 
@@ -59,9 +58,10 @@
     mFrameRecords[mOffset].actualPresentTime = presentTime;
 }
 
-void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) {
+void FrameTracker::setActualPresentFence(
+        std::shared_ptr<FenceTime>&& readyFence) {
     Mutex::Autolock lock(mMutex);
-    mFrameRecords[mOffset].actualPresentFence = readyFence;
+    mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
     mNumFences++;
 }
 
@@ -95,10 +95,6 @@
         mFrameRecords[mOffset].actualPresentFence = NULL;
         mNumFences--;
     }
-
-    // Clean up the signaled fences to keep the number of open fence FDs in
-    // this process reasonable.
-    processFencesLocked();
 }
 
 void FrameTracker::clearStats() {
@@ -107,8 +103,8 @@
         mFrameRecords[i].desiredPresentTime = 0;
         mFrameRecords[i].frameReadyTime = 0;
         mFrameRecords[i].actualPresentTime = 0;
-        mFrameRecords[i].frameReadyFence.clear();
-        mFrameRecords[i].actualPresentFence.clear();
+        mFrameRecords[i].frameReadyFence.reset();
+        mFrameRecords[i].actualPresentFence.reset();
     }
     mNumFences = 0;
     mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -156,7 +152,7 @@
         size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
         bool updated = false;
 
-        const sp<Fence>& rfence = records[idx].frameReadyFence;
+        const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
         if (rfence != NULL) {
             records[idx].frameReadyTime = rfence->getSignalTime();
             if (records[idx].frameReadyTime < INT64_MAX) {
@@ -166,7 +162,8 @@
             }
         }
 
-        const sp<Fence>& pfence = records[idx].actualPresentFence;
+        const std::shared_ptr<FenceTime>& pfence =
+                records[idx].actualPresentFence;
         if (pfence != NULL) {
             records[idx].actualPresentTime = pfence->getSignalTime();
             if (records[idx].actualPresentTime < INT64_MAX) {
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index cd5e3f3..adcdfb5 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_FRAMETRACKER_H
 #define ANDROID_FRAMETRACKER_H
 
+#include <ui/FenceTime.h>
+
 #include <stddef.h>
 
 #include <utils/Mutex.h>
@@ -26,7 +28,6 @@
 namespace android {
 
 class String8;
-class Fence;
 
 // FrameTracker tracks information about the most recently rendered frames. It
 // uses a circular buffer of frame records, and is *NOT* thread-safe -
@@ -60,7 +61,7 @@
 
     // setFrameReadyFence sets the fence that is used to get the time at which
     // the current frame became ready to be presented to the user.
-    void setFrameReadyFence(const sp<Fence>& readyFence);
+    void setFrameReadyFence(std::shared_ptr<FenceTime>&& readyFence);
 
     // setActualPresentTime sets the timestamp at which the current frame became
     // visible to the user.
@@ -68,7 +69,7 @@
 
     // setActualPresentFence sets the fence that is used to get the time
     // at which the current frame became visible to the user.
-    void setActualPresentFence(const sp<Fence>& fence);
+    void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
 
     // setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
     // This is used to compute frame presentation duration statistics relative
@@ -100,8 +101,8 @@
         nsecs_t desiredPresentTime;
         nsecs_t frameReadyTime;
         nsecs_t actualPresentTime;
-        sp<Fence> frameReadyFence;
-        sp<Fence> actualPresentFence;
+        std::shared_ptr<FenceTime> frameReadyFence;
+        std::shared_ptr<FenceTime> actualPresentFence;
     };
 
     // processFences iterates over all the frame records that have a fence set
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
index 70d9682..71052fb 100644
--- a/services/surfaceflinger/GpuService.cpp
+++ b/services/surfaceflinger/GpuService.cpp
@@ -16,6 +16,7 @@
 
 #include "GpuService.h"
 
+#include <binder/IResultReceiver.h>
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 #include <vkjson.h>
@@ -27,7 +28,7 @@
 class BpGpuService : public BpInterface<IGpuService>
 {
 public:
-    BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
+    explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
 };
 
 IMPLEMENT_META_INTERFACE(GpuService, "android.ui.IGpuService");
@@ -35,6 +36,7 @@
 status_t BnGpuService::onTransact(uint32_t code, const Parcel& data,
         Parcel* reply, uint32_t flags)
 {
+    status_t status;
     switch (code) {
     case SHELL_COMMAND_TRANSACTION: {
         int in = data.readFileDescriptor();
@@ -45,7 +47,16 @@
         for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
            args.add(data.readString16());
         }
-        return shellCommand(in, out, err, args);
+        sp<IBinder> unusedCallback;
+        sp<IResultReceiver> resultReceiver;
+        if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK)
+            return status;
+        if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK)
+            return status;
+        status = shellCommand(in, out, err, args);
+        if (resultReceiver != nullptr)
+            resultReceiver->send(status);
+        return OK;
     }
 
     default:
@@ -71,12 +82,15 @@
     for (size_t i = 0, n = args.size(); i < n; i++)
         ALOGV("  arg[%zu]: '%s'", i, String8(args[i]).string());
 
-    if (args[0] == String16("vkjson"))
-        return cmd_vkjson(out, err);
-    else if (args[0] == String16("help"))
-        return cmd_help(out);
-
-    return NO_ERROR;
+    if (args.size() >= 1) {
+        if (args[0] == String16("vkjson"))
+            return cmd_vkjson(out, err);
+        if (args[0] == String16("help"))
+            return cmd_help(out);
+    }
+    // no command, or unrecognized command
+    cmd_help(err);
+    return BAD_VALUE;
 }
 
 // ----------------------------------------------------------------------------
@@ -92,82 +106,27 @@
     }
     fprintf(outs,
         "GPU Service commands:\n"
-        "  vkjson   dump Vulkan device capabilities as JSON\n");
+        "  vkjson   dump Vulkan properties as JSON\n");
     fclose(outs);
     return NO_ERROR;
 }
 
-VkResult vkjsonPrint(FILE* out, FILE* err) {
-    VkResult result;
-
-    const VkApplicationInfo app_info = {
-        VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
-        "vkjson", 1,    /* app name, version */
-        "", 0,          /* engine name, version */
-        VK_API_VERSION_1_0
-    };
-    const VkInstanceCreateInfo instance_info = {
-        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr,
-        0,              /* flags */
-        &app_info,
-        0, nullptr,     /* layers */
-        0, nullptr,     /* extensions */
-    };
-    VkInstance instance;
-    result = vkCreateInstance(&instance_info, nullptr, &instance);
-    if (result != VK_SUCCESS) {
-        fprintf(err, "vkCreateInstance failed: %d\n", result);
-        return result;
-    }
-
-    uint32_t ngpu = 0;
-    result = vkEnumeratePhysicalDevices(instance, &ngpu, nullptr);
-    if (result != VK_SUCCESS) {
-        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
-        return result;
-    }
-    std::vector<VkPhysicalDevice> gpus(ngpu, VK_NULL_HANDLE);
-    result = vkEnumeratePhysicalDevices(instance, &ngpu, gpus.data());
-    if (result != VK_SUCCESS) {
-        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
-        return result;
-    }
-
-    for (size_t i = 0, n = gpus.size(); i < n; i++) {
-        auto props = VkJsonGetAllProperties(gpus[i]);
-        std::string json = VkJsonAllPropertiesToJson(props);
-        fwrite(json.data(), 1, json.size(), out);
-        if (i < n - 1)
-            fputc(',', out);
-        fputc('\n', out);
-    }
-
-    vkDestroyInstance(instance, nullptr);
-
-    return VK_SUCCESS;
+void vkjsonPrint(FILE* out) {
+    std::string json = VkJsonInstanceToJson(VkJsonGetInstance());
+    fwrite(json.data(), 1, json.size(), out);
+    fputc('\n', out);
 }
 
-status_t cmd_vkjson(int out, int err) {
-    int errnum;
+status_t cmd_vkjson(int out, int /*err*/) {
     FILE* outs = fdopen(out, "w");
     if (!outs) {
-        errnum = errno;
+        int errnum = errno;
         ALOGE("vkjson: failed to create output stream: %s", strerror(errnum));
         return -errnum;
     }
-    FILE* errs = fdopen(err, "w");
-    if (!errs) {
-        errnum = errno;
-        ALOGE("vkjson: failed to create error stream: %s", strerror(errnum));
-        fclose(outs);
-        return -errnum;
-    }
-    fprintf(outs, "[\n");
-    VkResult result = vkjsonPrint(outs, errs);
-    fprintf(outs, "]\n");
-    fclose(errs);
+    vkjsonPrint(outs);
     fclose(outs);
-    return result >= 0 ? NO_ERROR : UNKNOWN_ERROR;
+    return NO_ERROR;
 }
 
 } // anonymous namespace
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3ffa655..88a5bd4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -38,12 +38,14 @@
 #include <ui/PixelFormat.h>
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 
 #include "clz.h"
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
@@ -75,11 +77,14 @@
         mPendingStates(),
         mQueuedFrames(0),
         mSidebandStreamChanged(false),
+        mActiveBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mOverrideScalingMode(-1),
         mCurrentOpacity(true),
+        mBufferLatched(false),
         mCurrentFrameNumber(0),
+        mPreviousFrameNumber(0),
         mRefreshPending(false),
         mFrameLatencyNeeded(false),
         mFiltering(false),
@@ -98,7 +103,7 @@
         mLastFrameNumberReceived(0),
         mUpdateTexImageFailed(false),
         mAutoRefresh(false),
-        mFreezePositionUpdates(false)
+        mFreezeGeometryUpdates(false)
 {
 #ifdef USE_HWC2
     ALOGV("Creating Layer %s", name.string());
@@ -126,6 +131,8 @@
     mCurrentState.active.transform.set(0, 0);
     mCurrentState.crop.makeInvalid();
     mCurrentState.finalCrop.makeInvalid();
+    mCurrentState.requestedFinalCrop = mCurrentState.finalCrop;
+    mCurrentState.requestedCrop = mCurrentState.crop;
     mCurrentState.z = 0;
 #ifdef USE_HWC2
     mCurrentState.alpha = 1.0f;
@@ -136,6 +143,9 @@
     mCurrentState.flags = layerFlags;
     mCurrentState.sequence = 0;
     mCurrentState.requested = mCurrentState.active;
+    mCurrentState.dataSpace = HAL_DATASPACE_UNKNOWN;
+    mCurrentState.appId = 0;
+    mCurrentState.type = 0;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -149,25 +159,26 @@
             flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
 #endif
     mFrameTracker.setDisplayRefreshPeriod(displayPeriod);
+
+    CompositorTiming compositorTiming;
+    flinger->getCompositorTiming(&compositorTiming);
+    mFrameEventHistory.initializeCompositorTiming(compositorTiming);
 }
 
 void Layer::onFirstRef() {
     // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-    mProducer = new MonitoredProducer(producer, mFlinger);
-    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
-            this);
+    BufferQueue::createBufferQueue(&producer, &consumer, true);
+    mProducer = new MonitoredProducer(producer, mFlinger, this);
+    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName, this);
     mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
     mSurfaceFlingerConsumer->setContentsChangedListener(this);
     mSurfaceFlingerConsumer->setName(mName);
 
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
-#warning "disabling triple buffering"
-#else
-    mProducer->setMaxDequeuedBufferCount(2);
-#endif
+    if (mFlinger->isLayerTripleBufferingDisabled()) {
+        mProducer->setMaxDequeuedBufferCount(2);
+    }
 
     const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
     updateTransformHint(hw);
@@ -214,7 +225,8 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         Mutex::Autolock lock(mQueueItemLock);
-
+        mFlinger->mInterceptor.saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+                item.mGraphicBuffer->getHeight(), item.mFrameNumber);
         // Reset the frame number tracker when we receive the first buffer after
         // a frame number reset
         if (item.mFrameNumber == 1) {
@@ -277,7 +289,23 @@
 // the layer has been remove from the current state list (and just before
 // it's removed from the drawing state list)
 void Layer::onRemoved() {
+    if (mCurrentState.zOrderRelativeOf != nullptr) {
+        sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote();
+        if (strongRelative != nullptr) {
+            strongRelative->removeZOrderRelative(this);
+        }
+        mCurrentState.zOrderRelativeOf = nullptr;
+    }
+
     mSurfaceFlingerConsumer->abandon();
+
+#ifdef USE_HWC2
+    clearHwcLayers();
+#endif
+
+    for (const auto& child : mCurrentChildren) {
+        child->onRemoved();
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -314,22 +342,6 @@
     return NO_ERROR;
 }
 
-/*
- * The layer handle is just a BBinder object passed to the client
- * (remote process) -- we don't keep any reference on our side such that
- * the dtor is called when the remote side let go of its reference.
- *
- * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
- * this layer when the handle is destroyed.
- */
-class Layer::Handle : public BBinder, public LayerCleaner {
-    public:
-        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-            : LayerCleaner(flinger, layer), owner(layer) {}
-
-        wp<Layer> owner;
-};
-
 sp<IBinder> Layer::getHandle() {
     Mutex::Autolock _l(mLock);
 
@@ -376,6 +388,44 @@
     return Region(win).subtract(exclude).getBounds();
 }
 
+Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
+    const Layer::State& s(getDrawingState());
+    Rect win(s.active.w, s.active.h);
+
+    if (!s.crop.isEmpty()) {
+        win.intersect(s.crop, &win);
+    }
+
+    Transform t = getTransform();
+    win = t.transform(win);
+
+    if (!s.finalCrop.isEmpty()) {
+        win.intersect(s.finalCrop, &win);
+    }
+
+    const sp<Layer>& p = mDrawingParent.promote();
+    // Now we need to calculate the parent bounds, so we can clip ourselves to those.
+    // When calculating the parent bounds for purposes of clipping,
+    // we don't need to constrain the parent to its transparent region.
+    // The transparent region is an optimization based on the
+    // buffer contents of the layer, but does not affect the space allocated to
+    // it by policy, and thus children should be allowed to extend into the
+    // parent's transparent region. In fact one of the main uses, is to reduce
+    // buffer allocation size in cases where a child window sits behind a main window
+    // (by marking the hole in the parent window as a transparent region)
+    if (p != nullptr) {
+        Rect bounds = p->computeScreenBounds(false);
+        bounds.intersect(win, &win);
+    }
+
+    if (reduceTransparentRegion) {
+        auto const screenTransparentRegion = t.transform(s.activeTransparentRegion);
+        win = reduce(win, screenTransparentRegion);
+    }
+
+    return win;
+}
+
 Rect Layer::computeBounds() const {
     const Layer::State& s(getDrawingState());
     return computeBounds(s.activeTransparentRegion);
@@ -388,15 +438,27 @@
     if (!s.crop.isEmpty()) {
         win.intersect(s.crop, &win);
     }
+
+    Rect bounds = win;
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        // Look in computeScreenBounds recursive call for explanation of
+        // why we pass false here.
+        bounds = p->computeScreenBounds(false /* reduceTransparentRegion */);
+    }
+
+    Transform t = getTransform();
+    if (p != nullptr) {
+        win = t.transform(win);
+        win.intersect(bounds, &win);
+        win = t.inverse().transform(win);
+    }
+
     // subtract the transparent region and snap to the bounds
     return reduce(win, activeTransparentRegion);
 }
 
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
-    // the content crop is the area of the content that gets scaled to the
-    // layer's size.
-    FloatRect crop(getContentCrop());
-
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
     // the crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(getDrawingState());
@@ -413,7 +475,8 @@
         activeCrop = s.crop;
     }
 
-    activeCrop = s.active.transform.transform(activeCrop);
+    Transform t = getTransform();
+    activeCrop = t.transform(activeCrop);
     if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
         activeCrop.clear();
     }
@@ -422,7 +485,27 @@
             activeCrop.clear();
         }
     }
-    activeCrop = s.active.transform.inverse().transform(activeCrop);
+    return activeCrop;
+}
+
+FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+    // the content crop is the area of the content that gets scaled to the
+    // layer's size. This is in buffer space.
+    FloatRect crop = getContentCrop().toFloatRect();
+
+    // In addition there is a WM-specified crop we pull from our drawing state.
+    const State& s(getDrawingState());
+
+    // Screen space to make reduction to parent crop clearer.
+    Rect activeCrop = computeInitialCrop(hw);
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        auto parentCrop = p->computeInitialCrop(hw);
+        activeCrop.intersect(parentCrop, &activeCrop);
+    }
+    Transform t = getTransform();
+    // Back to layer space to work with the content crop.
+    activeCrop = t.inverse().transform(activeCrop);
 
     // This needs to be here as transform.transform(Rect) computes the
     // transformed rect and then takes the bounding box of the result before
@@ -441,7 +524,7 @@
     // which means using the inverse of the current transform set on the
     // SurfaceFlingerConsumer.
     uint32_t invTransform = mCurrentTransform;
-    if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
+    if (getTransformToDisplayInverse()) {
         /*
          * the code below applies the primary display's inverse transform to the
          * buffer
@@ -496,7 +579,7 @@
 }
 
 #ifdef USE_HWC2
-void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice)
+void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z)
 #else
 void Layer::setGeometry(
     const sp<const DisplayDevice>& hw,
@@ -530,16 +613,17 @@
     // this gives us only the "orientation" component of the transform
     const State& s(getDrawingState());
 #ifdef USE_HWC2
-    if (!isOpaque(s) || s.alpha != 1.0f) {
-        auto blendMode = mPremultipliedAlpha ?
+    auto blendMode = HWC2::BlendMode::None;
+    if (!isOpaque(s) || getAlpha() != 1.0f) {
+        blendMode = mPremultipliedAlpha ?
                 HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
-        auto error = hwcLayer->setBlendMode(blendMode);
-        ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set blend mode %s:"
-                " %s (%d)", mName.string(), to_string(blendMode).c_str(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
     }
+    auto error = hwcLayer->setBlendMode(blendMode);
+    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set blend mode %s:"
+             " %s (%d)", mName.string(), to_string(blendMode).c_str(),
+             to_string(error).c_str(), static_cast<int32_t>(error));
 #else
-    if (!isOpaque(s) || s.alpha != 0xFF) {
+    if (!isOpaque(s) || getAlpha() != 0xFF) {
         layer.setBlending(mPremultipliedAlpha ?
                 HWC_BLENDING_PREMULT :
                 HWC_BLENDING_COVERAGE);
@@ -549,9 +633,10 @@
     // apply the layer's transform, followed by the display's global transform
     // here we're guaranteed that the layer's transform preserves rects
     Region activeTransparentRegion(s.activeTransparentRegion);
+    Transform t = getTransform();
     if (!s.crop.isEmpty()) {
         Rect activeCrop(s.crop);
-        activeCrop = s.active.transform.transform(activeCrop);
+        activeCrop = t.transform(activeCrop);
 #ifdef USE_HWC2
         if(!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
 #else
@@ -559,7 +644,7 @@
 #endif
             activeCrop.clear();
         }
-        activeCrop = s.active.transform.inverse().transform(activeCrop, true);
+        activeCrop = t.inverse().transform(activeCrop, true);
         // This needs to be here as transform.transform(Rect) computes the
         // transformed rect and then takes the bounding box of the result before
         // returning. This means
@@ -578,7 +663,8 @@
         activeTransparentRegion.orSelf(Rect(activeCrop.right, activeCrop.top,
                 s.active.w, activeCrop.bottom));
     }
-    Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion)));
+
+    Rect frame(t.transform(computeBounds(activeTransparentRegion)));
     if (!s.finalCrop.isEmpty()) {
         if(!frame.intersect(s.finalCrop, &frame)) {
             frame.clear();
@@ -590,7 +676,7 @@
     }
     const Transform& tr(displayDevice->getTransform());
     Rect transformedFrame = tr.transform(frame);
-    auto error = hwcLayer->setDisplayFrame(transformedFrame);
+    error = hwcLayer->setDisplayFrame(transformedFrame);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
                 mName.string(), transformedFrame.left, transformedFrame.top,
@@ -611,15 +697,29 @@
         hwcInfo.sourceCrop = sourceCrop;
     }
 
-    error = hwcLayer->setPlaneAlpha(s.alpha);
+    float alpha = getAlpha();
+    error = hwcLayer->setPlaneAlpha(alpha);
     ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set plane alpha %.3f: "
-            "%s (%d)", mName.string(), s.alpha, to_string(error).c_str(),
+            "%s (%d)", mName.string(), alpha, to_string(error).c_str(),
             static_cast<int32_t>(error));
 
-    error = hwcLayer->setZOrder(s.z);
+    error = hwcLayer->setZOrder(z);
     ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)",
-            mName.string(), s.z, to_string(error).c_str(),
+            mName.string(), z, to_string(error).c_str(),
             static_cast<int32_t>(error));
+
+    int type = s.type;
+    int appId = s.appId;
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent.get()) {
+        auto& parentState = parent->getDrawingState();
+        type = parentState.type;
+        appId = parentState.appId;
+    }
+
+    error = hwcLayer->setInfo(type, appId);
+    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)",
+             mName.string(), static_cast<int32_t>(error));
 #else
     if (!frame.intersect(hw->getViewport(), &frame)) {
         frame.clear();
@@ -627,7 +727,7 @@
     const Transform& tr(hw->getTransform());
     layer.setFrame(tr.transform(frame));
     layer.setCrop(computeCrop(hw));
-    layer.setPlaneAlpha(s.alpha);
+    layer.setPlaneAlpha(getAlpha());
 #endif
 
     /*
@@ -639,9 +739,9 @@
      */
 
     const Transform bufferOrientation(mCurrentTransform);
-    Transform transform(tr * s.active.transform * bufferOrientation);
+    Transform transform(tr * t * bufferOrientation);
 
-    if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
+    if (getTransformToDisplayInverse()) {
         /*
          * the code below applies the primary display's inverse transform to the
          * buffer
@@ -653,8 +753,14 @@
             invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
                     NATIVE_WINDOW_TRANSFORM_FLIP_H;
         }
-        // and apply to the current transform
-        transform = Transform(invTransform) * transform;
+
+        /*
+         * Here we cancel out the orientation component of the WM transform.
+         * The scaling and translate components are already included in our bounds
+         * computation so it's enough to just omit it in the composition.
+         * See comment in onDraw with ref to b/36727915 for why.
+         */
+        transform = Transform(invTransform) * tr * bufferOrientation;
     }
 
     // this gives us only the "orientation" component of the transform
@@ -689,9 +795,7 @@
 
     mHwcLayers[hwcId].forceClientComposition = true;
 }
-#endif
 
-#ifdef USE_HWC2
 void Layer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
     // Apply this display's projection's viewport to the visible region
     // before giving it to the HWC HAL.
@@ -699,7 +803,8 @@
     const auto& viewport = displayDevice->getViewport();
     Region visible = tr.transform(visibleRegion.intersect(viewport));
     auto hwcId = displayDevice->getHwcDisplayId();
-    auto& hwcLayer = mHwcLayers[hwcId].layer;
+    auto& hwcInfo = mHwcLayers[hwcId];
+    auto& hwcLayer = hwcInfo.layer;
     auto error = hwcLayer->setVisibleRegion(visible);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
@@ -728,7 +833,7 @@
     }
 
     // Client layers
-    if (mHwcLayers[hwcId].forceClientComposition ||
+    if (hwcInfo.forceClientComposition ||
             (mActiveBuffer != nullptr && mActiveBuffer->handle == nullptr)) {
         ALOGV("[%s] Requesting Client composition", mName.string());
         setCompositionType(hwcId, HWC2::Composition::Client);
@@ -766,14 +871,31 @@
         setCompositionType(hwcId, HWC2::Composition::Device);
     }
 
+    ALOGV("setPerFrameData: dataspace = %d", mCurrentState.dataSpace);
+    error = hwcLayer->setDataspace(mCurrentState.dataSpace);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(),
+              mCurrentState.dataSpace, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer,
+            &hwcSlot, &hwcBuffer);
+
     auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence();
-    error = hwcLayer->setBuffer(mActiveBuffer->handle, acquireFence);
+    error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
                 mActiveBuffer->handle, to_string(error).c_str(),
                 static_cast<int32_t>(error));
     }
 }
+
+android_dataspace Layer::getDataSpace() const {
+    return mCurrentState.dataSpace;
+}
 #else
 void Layer::setPerFrameData(const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface& layer) {
@@ -817,7 +939,7 @@
     }
     // Subtract the transparent region and snap to the bounds
     Rect bounds = reduce(win, s.activeTransparentRegion);
-    Rect frame(s.active.transform.transform(bounds));
+    Rect frame(getTransform().transform(bounds));
     frame.intersect(displayDevice->getViewport(), &frame);
     if (!s.finalCrop.isEmpty()) {
         frame.intersect(s.finalCrop, &frame);
@@ -866,7 +988,7 @@
     }
     // subtract the transparent region and snap to the bounds
     Rect bounds = reduce(win, s.activeTransparentRegion);
-    Rect frame(s.active.transform.transform(bounds));
+    Rect frame(getTransform().transform(bounds));
     frame.intersect(hw->getViewport(), &frame);
     if (!s.finalCrop.isEmpty()) {
         frame.intersect(s.finalCrop, &frame);
@@ -893,6 +1015,27 @@
     onDraw(hw, Region(hw->bounds()), false);
 }
 
+static constexpr mat4 inverseOrientation(uint32_t transform) {
+    const mat4 flipH(-1,0,0,0,  0,1,0,0, 0,0,1,0, 1,0,0,1);
+    const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1);
+    const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1);
+    mat4 tr;
+
+    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        tr = tr * rot90;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+        tr = tr * flipH;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+        tr = tr * flipV;
+    }
+    return inverse(tr);
+}
+
+/*
+ * onDraw will draw the current layer onto the presentable buffer
+ */
 void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
         bool useIdentityTransform) const
 {
@@ -909,19 +1052,18 @@
 
         // figure out if there is something below us
         Region under;
-        const SurfaceFlinger::LayerVector& drawingLayers(
-                mFlinger->mDrawingState.layersSortedByZ);
-        const size_t count = drawingLayers.size();
-        for (size_t i=0 ; i<count ; ++i) {
-            const sp<Layer>& layer(drawingLayers[i]);
-            if (layer.get() == static_cast<Layer const*>(this))
-                break;
+        bool finished = false;
+        mFlinger->mDrawingState.traverseInZOrder([&](Layer* layer) {
+            if (finished || layer == static_cast<Layer const*>(this)) {
+                finished = true;
+                return;
+            }
             under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
-        }
+        });
         // if not everything below us is covered, we plug the holes!
         Region holes(clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearWithOpenGL(hw, holes, 0, 0, 0, 1);
+            clearWithOpenGL(hw, 0, 0, 0, 1);
         }
         return;
     }
@@ -948,30 +1090,30 @@
         mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
         mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
 
-        if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
+        if (getTransformToDisplayInverse()) {
 
             /*
              * the code below applies the primary display's inverse transform to
              * the texture transform
              */
-
-            // create a 4x4 transform matrix from the display transform flags
-            const mat4 flipH(-1,0,0,0,  0,1,0,0, 0,0,1,0, 1,0,0,1);
-            const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1);
-            const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1);
-
-            mat4 tr;
             uint32_t transform =
                     DisplayDevice::getPrimaryDisplayOrientationTransform();
-            if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90)
-                tr = tr * rot90;
-            if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H)
-                tr = tr * flipH;
-            if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V)
-                tr = tr * flipV;
+            mat4 tr = inverseOrientation(transform);
 
-            // calculate the inverse
-            tr = inverse(tr);
+            /**
+             * TODO(b/36727915): This is basically a hack.
+             *
+             * Ensure that regardless of the parent transformation,
+             * this buffer is always transformed from native display
+             * orientation to display orientation. For example, in the case
+             * of a camera where the buffer remains in native orientation,
+             * we want the pixels to always be upright.
+             */
+            sp<Layer> p = mDrawingParent.promote();
+            if (p != nullptr) {
+                const auto parentTransform = p->getTransform();
+                tr = tr * inverseOrientation(parentTransform.getOrientation());
+            }
 
             // and finally apply it to the original texture matrix
             const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
@@ -987,13 +1129,13 @@
     } else {
         engine.setupLayerBlackedOut();
     }
-    drawWithOpenGL(hw, clip, useIdentityTransform);
+    drawWithOpenGL(hw, useIdentityTransform);
     engine.disableTexturing();
 }
 
 
 void Layer::clearWithOpenGL(const sp<const DisplayDevice>& hw,
-        const Region& /* clip */, float red, float green, float blue,
+        float red, float green, float blue,
         float alpha) const
 {
     RenderEngine& engine(mFlinger->getRenderEngine());
@@ -1003,12 +1145,12 @@
 }
 
 void Layer::clearWithOpenGL(
-        const sp<const DisplayDevice>& hw, const Region& clip) const {
-    clearWithOpenGL(hw, clip, 0,0,0,0);
+        const sp<const DisplayDevice>& hw) const {
+    clearWithOpenGL(hw, 0,0,0,0);
 }
 
 void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw,
-        const Region& /* clip */, bool useIdentityTransform) const {
+        bool useIdentityTransform) const {
     const State& s(getDrawingState());
 
     computeGeometry(hw, mMesh, useIdentityTransform);
@@ -1029,12 +1171,13 @@
      */
     Rect win(computeBounds());
 
+    Transform t = getTransform();
     if (!s.finalCrop.isEmpty()) {
-        win = s.active.transform.transform(win);
+        win = t.transform(win);
         if (!win.intersect(s.finalCrop, &win)) {
             win.clear();
         }
-        win = s.active.transform.inverse().transform(win);
+        win = t.inverse().transform(win);
         if (!win.intersect(computeBounds(), &win)) {
             win.clear();
         }
@@ -1054,7 +1197,10 @@
     texCoords[3] = vec2(right, 1.0f - top);
 
     RenderEngine& engine(mFlinger->getRenderEngine());
-    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha);
+    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), getAlpha());
+#ifdef USE_HWC2
+    engine.setSourceDataSpace(mCurrentState.dataSpace);
+#endif
     engine.drawMesh(mMesh);
     engine.disableBlending();
 }
@@ -1204,6 +1350,8 @@
     switch (format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_BGRA_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
             return false;
     }
     // in all other case, we have no blending (also for unknown formats)
@@ -1233,25 +1381,21 @@
         bool useIdentityTransform) const
 {
     const Layer::State& s(getDrawingState());
-    const Transform tr(hw->getTransform());
+    const Transform hwTransform(hw->getTransform());
     const uint32_t hw_h = hw->getHeight();
-    Rect win(s.active.w, s.active.h);
-    if (!s.crop.isEmpty()) {
-        win.intersect(s.crop, &win);
-    }
-    // subtract the transparent region and snap to the bounds
-    win = reduce(win, s.activeTransparentRegion);
+    Rect win = computeBounds();
 
     vec2 lt = vec2(win.left, win.top);
     vec2 lb = vec2(win.left, win.bottom);
     vec2 rb = vec2(win.right, win.bottom);
     vec2 rt = vec2(win.right, win.top);
 
+    Transform layerTransform = getTransform();
     if (!useIdentityTransform) {
-        lt = s.active.transform.transform(lt);
-        lb = s.active.transform.transform(lb);
-        rb = s.active.transform.transform(rb);
-        rt = s.active.transform.transform(rt);
+        lt = layerTransform.transform(lt);
+        lb = layerTransform.transform(lb);
+        rb = layerTransform.transform(rb);
+        rt = layerTransform.transform(rt);
     }
 
     if (!s.finalCrop.isEmpty()) {
@@ -1262,10 +1406,10 @@
     }
 
     Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-    position[0] = tr.transform(lt);
-    position[1] = tr.transform(lb);
-    position[2] = tr.transform(rb);
-    position[3] = tr.transform(rt);
+    position[0] = hwTransform.transform(lt);
+    position[1] = hwTransform.transform(lb);
+    position[2] = hwTransform.transform(rb);
+    position[3] = hwTransform.transform(rt);
     for (size_t i=0 ; i<4 ; i++) {
         position[i].y = hw_h - position[i].y;
     }
@@ -1336,29 +1480,23 @@
 
     // If this transaction is waiting on the receipt of a frame, generate a sync
     // point and send it to the remote layer.
-    if (mCurrentState.handle != nullptr) {
-        sp<IBinder> strongBinder = mCurrentState.handle.promote();
-        sp<Handle> handle = nullptr;
-        sp<Layer> handleLayer = nullptr;
-        if (strongBinder != nullptr) {
-            handle = static_cast<Handle*>(strongBinder.get());
-            handleLayer = handle->owner.promote();
-        }
-        if (strongBinder == nullptr || handleLayer == nullptr) {
-            ALOGE("[%s] Unable to promote Layer handle", mName.string());
+    if (mCurrentState.barrierLayer != nullptr) {
+        sp<Layer> barrierLayer = mCurrentState.barrierLayer.promote();
+        if (barrierLayer == nullptr) {
+            ALOGE("[%s] Unable to promote barrier Layer.", mName.string());
             // If we can't promote the layer we are intended to wait on,
             // then it is expired or otherwise invalid. Allow this transaction
             // to be applied as per normal (no synchronization).
-            mCurrentState.handle = nullptr;
+            mCurrentState.barrierLayer = nullptr;
         } else {
             auto syncPoint = std::make_shared<SyncPoint>(
                     mCurrentState.frameNumber);
-            if (handleLayer->addSyncPoint(syncPoint)) {
+            if (barrierLayer->addSyncPoint(syncPoint)) {
                 mRemoteSyncPoints.push_back(std::move(syncPoint));
             } else {
                 // We already missed the frame we're supposed to synchronize
                 // on, so go ahead and apply the state update
-                mCurrentState.handle = nullptr;
+                mCurrentState.barrierLayer = nullptr;
             }
         }
 
@@ -1381,7 +1519,7 @@
 bool Layer::applyPendingStates(State* stateToCommit) {
     bool stateUpdateAvailable = false;
     while (!mPendingStates.empty()) {
-        if (mPendingStates[0].handle != nullptr) {
+        if (mPendingStates[0].barrierLayer != nullptr) {
             if (mRemoteSyncPoints.empty()) {
                 // If we don't have a sync point for this, apply it anyway. It
                 // will be visually wrong, but it should keep us from getting
@@ -1510,12 +1648,25 @@
         }
     }
 
-    // always set active to requested, unless we're asked not to
-    // this is used by Layer, which special cases resizes.
-    if (flags & eDontUpdateGeometryState)  {
-    } else {
+    // Here we apply various requested geometry states, depending on our
+    // latching configuration. See Layer.h for a detailed discussion of
+    // how geometry latching is controlled.
+    if (!(flags & eDontUpdateGeometryState)) {
         Layer::State& editCurrentState(getCurrentState());
-        if (mFreezePositionUpdates) {
+
+        // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize
+        // mode, which causes attributes which normally latch regardless of scaling mode,
+        // to be delayed. We copy the requested state to the active state making sure
+        // to respect these rules (again see Layer.h for a detailed discussion).
+        //
+        // There is an awkward asymmetry in the handling of the crop states in the position
+        // states, as can be seen below. Largely this arises from position and transform
+        // being stored in the same data structure while having different latching rules.
+        // b/38182305
+        //
+        // Careful that "c" and editCurrentState may not begin as equivalent due to
+        // applyPendingStates in the presence of deferred transactions.
+        if (mFreezeGeometryUpdates) {
             float tx = c.active.transform.tx();
             float ty = c.active.transform.ty();
             c.active = c.requested;
@@ -1547,11 +1698,7 @@
     // that transactions for layers depending on this layer's frames becoming
     // visible are not blocked
     if (c.flags & layer_state_t::eLayerHidden) {
-        Mutex::Autolock lock(mLocalSyncPointMutex);
-        for (auto& point : mLocalSyncPoints) {
-            point->setFrameAvailable();
-        }
-        mLocalSyncPoints.clear();
+        clearSyncPoints();
     }
 
     // Commit the transaction
@@ -1580,25 +1727,87 @@
     // we want to apply the position portion of the transform matrix immediately,
     // but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
     mCurrentState.requested.transform.set(x, y);
-    if (immediate && !mFreezePositionUpdates) {
+    if (immediate && !mFreezeGeometryUpdates) {
+        // Here we directly update the active state
+        // unlike other setters, because we store it within
+        // the transform, but use different latching rules.
+        // b/38182305
         mCurrentState.active.transform.set(x, y);
     }
-    mFreezePositionUpdates = mFreezePositionUpdates || !immediate;
+    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
 
-bool Layer::setLayer(uint32_t z) {
+bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) {
+    ssize_t idx = mCurrentChildren.indexOf(childLayer);
+    if (idx < 0) {
+        return false;
+    }
+    if (childLayer->setLayer(z)) {
+        mCurrentChildren.removeAt(idx);
+        mCurrentChildren.add(childLayer);
+    }
+    return true;
+}
+
+bool Layer::setLayer(int32_t z) {
     if (mCurrentState.z == z)
         return false;
     mCurrentState.sequence++;
     mCurrentState.z = z;
     mCurrentState.modified = true;
+
+    // Discard all relative layering.
+    if (mCurrentState.zOrderRelativeOf != nullptr) {
+        sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote();
+        if (strongRelative != nullptr) {
+            strongRelative->removeZOrderRelative(this);
+        }
+        mCurrentState.zOrderRelativeOf = nullptr;
+    }
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
+void Layer::removeZOrderRelative(const wp<Layer>& relative) {
+    mCurrentState.zOrderRelatives.remove(relative);
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+}
+
+void Layer::addZOrderRelative(const wp<Layer>& relative) {
+    mCurrentState.zOrderRelatives.add(relative);
+    mCurrentState.modified = true;
+    mCurrentState.sequence++;
+    setTransactionFlags(eTransactionNeeded);
+}
+
+bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t z) {
+    sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
+    if (handle == nullptr) {
+        return false;
+    }
+    sp<Layer> relative = handle->owner.promote();
+    if (relative == nullptr) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    mCurrentState.z = z;
+
+    mCurrentState.zOrderRelativeOf = relative;
+    relative->addZOrderRelative(this);
+
+    setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
 bool Layer::setSize(uint32_t w, uint32_t h) {
     if (mCurrentState.requested.w == w && mCurrentState.requested.h == h)
         return false;
@@ -1624,7 +1833,7 @@
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
     mCurrentState.sequence++;
     mCurrentState.requested.transform.set(
-            matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
+            matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1648,22 +1857,30 @@
 }
 
 bool Layer::setCrop(const Rect& crop, bool immediate) {
-    if (mCurrentState.crop == crop)
+    if (mCurrentState.requestedCrop == crop)
         return false;
     mCurrentState.sequence++;
     mCurrentState.requestedCrop = crop;
-    if (immediate) {
+    if (immediate && !mFreezeGeometryUpdates) {
         mCurrentState.crop = crop;
     }
+    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setFinalCrop(const Rect& crop) {
-    if (mCurrentState.finalCrop == crop)
+
+bool Layer::setFinalCrop(const Rect& crop, bool immediate) {
+    if (mCurrentState.requestedFinalCrop == crop)
         return false;
     mCurrentState.sequence++;
-    mCurrentState.finalCrop = crop;
+    mCurrentState.requestedFinalCrop = crop;
+    if (immediate && !mFreezeGeometryUpdates) {
+        mCurrentState.finalCrop = crop;
+    }
+    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1677,6 +1894,13 @@
     return true;
 }
 
+void Layer::setInfo(uint32_t type, uint32_t appId) {
+  mCurrentState.appId = appId;
+  mCurrentState.type = type;
+  mCurrentState.modified = true;
+  setTransactionFlags(eTransactionNeeded);
+}
+
 uint32_t Layer::getEffectiveScalingMode() const {
     if (mOverrideScalingMode >= 0) {
       return mOverrideScalingMode;
@@ -1694,19 +1918,43 @@
     return true;
 }
 
-void Layer::deferTransactionUntil(const sp<IBinder>& handle,
+bool Layer::setDataSpace(android_dataspace dataSpace) {
+    if (mCurrentState.dataSpace == dataSpace)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.dataSpace = dataSpace;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+uint32_t Layer::getLayerStack() const {
+    auto p = mDrawingParent.promote();
+    if (p == nullptr) {
+        return getDrawingState().layerStack;
+    }
+    return p->getLayerStack();
+}
+
+void Layer::deferTransactionUntil(const sp<Layer>& barrierLayer,
         uint64_t frameNumber) {
-    mCurrentState.handle = handle;
+    mCurrentState.barrierLayer = barrierLayer;
     mCurrentState.frameNumber = frameNumber;
     // We don't set eTransactionNeeded, because just receiving a deferral
     // request without any other state updates shouldn't actually induce a delay
     mCurrentState.modified = true;
     pushPendingState();
-    mCurrentState.handle = nullptr;
+    mCurrentState.barrierLayer = nullptr;
     mCurrentState.frameNumber = 0;
     mCurrentState.modified = false;
 }
 
+void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle,
+        uint64_t frameNumber) {
+    sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
+    deferTransactionUntil(handle->owner.promote(), frameNumber);
+}
+
 void Layer::useSurfaceDamage() {
     if (mFlinger->mForceFullDamage) {
         surfaceDamageRegion = Region::INVALID_REGION;
@@ -1746,65 +1994,127 @@
     return isDue || !isPlausible;
 }
 
-bool Layer::onPreComposition() {
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+    if (mBufferLatched) {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
+    }
     mRefreshPending = false;
     return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
 }
 
-bool Layer::onPostComposition() {
-    bool frameLatencyNeeded = mFrameLatencyNeeded;
-    if (mFrameLatencyNeeded) {
-        nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
-        mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+bool Layer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
+        const std::shared_ptr<FenceTime>& presentFence,
+        const CompositorTiming& compositorTiming) {
+    mAcquireTimeline.updateSignalTimes();
+    mReleaseTimeline.updateSignalTimes();
 
-        sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
-        if (frameReadyFence->isValid()) {
-            mFrameTracker.setFrameReadyFence(frameReadyFence);
-        } else {
-            // There was no fence for this frame, so assume that it was ready
-            // to be presented at the desired present time.
-            mFrameTracker.setFrameReadyTime(desiredPresentTime);
-        }
+    // mFrameLatencyNeeded is true when a new frame was latched for the
+    // composition.
+    if (!mFrameLatencyNeeded)
+        return false;
 
-        const HWComposer& hwc = mFlinger->getHwComposer();
-#ifdef USE_HWC2
-        sp<Fence> presentFence = hwc.getRetireFence(HWC_DISPLAY_PRIMARY);
-#else
-        sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
-#endif
-        if (presentFence->isValid()) {
-            mFrameTracker.setActualPresentFence(presentFence);
-        } else {
-            // The HWC doesn't support present fences, so use the refresh
-            // timestamp instead.
-            nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
-            mFrameTracker.setActualPresentTime(presentTime);
-        }
-
-        mFrameTracker.advanceFrame();
-        mFrameLatencyNeeded = false;
+    // Update mFrameEventHistory.
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addPostComposition(mCurrentFrameNumber,
+                glDoneFence, presentFence, compositorTiming);
     }
-    return frameLatencyNeeded;
+
+    // Update mFrameTracker.
+    nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
+    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+    std::shared_ptr<FenceTime> frameReadyFence =
+            mSurfaceFlingerConsumer->getCurrentFenceTime();
+    if (frameReadyFence->isValid()) {
+        mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+    } else {
+        // There was no fence for this frame, so assume that it was ready
+        // to be presented at the desired present time.
+        mFrameTracker.setFrameReadyTime(desiredPresentTime);
+    }
+
+    if (presentFence->isValid()) {
+        mFrameTracker.setActualPresentFence(
+                std::shared_ptr<FenceTime>(presentFence));
+    } else {
+        // The HWC doesn't support present fences, so use the refresh
+        // timestamp instead.
+        mFrameTracker.setActualPresentTime(
+            mFlinger->getHwComposer().getRefreshTimestamp(
+                HWC_DISPLAY_PRIMARY));
+    }
+
+    mFrameTracker.advanceFrame();
+    mFrameLatencyNeeded = false;
+    return true;
 }
 
 #ifdef USE_HWC2
-void Layer::releasePendingBuffer() {
-    mSurfaceFlingerConsumer->releasePendingBuffer();
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    if (!mSurfaceFlingerConsumer->releasePendingBuffer()) {
+        return;
+    }
+
+    auto releaseFenceTime = std::make_shared<FenceTime>(
+            mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+    mReleaseTimeline.push(releaseFenceTime);
+
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (mPreviousFrameNumber != 0) {
+        mFrameEventHistory.addRelease(mPreviousFrameNumber,
+                dequeueReadyTime, std::move(releaseFenceTime));
+    }
 }
 #endif
 
+bool Layer::isHiddenByPolicy() const {
+    const Layer::State& s(mDrawingState);
+    const auto& parent = mDrawingParent.promote();
+    if (parent != nullptr && parent->isHiddenByPolicy()) {
+        return true;
+    }
+    return s.flags & layer_state_t::eLayerHidden;
+}
+
 bool Layer::isVisible() const {
-    const Layer::State& s(mDrawingState);
 #ifdef USE_HWC2
-    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha > 0.0f
+    return !(isHiddenByPolicy()) && getAlpha() > 0.0f
             && (mActiveBuffer != NULL || mSidebandStream != NULL);
 #else
-    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
+    return !(isHiddenByPolicy()) && getAlpha()
             && (mActiveBuffer != NULL || mSidebandStream != NULL);
 #endif
 }
 
-Region Layer::latchBuffer(bool& recomputeVisibleRegions)
+bool Layer::allTransactionsSignaled() {
+    auto headFrameNumber = getHeadFrameNumber();
+    bool matchingFramesFound = false;
+    bool allTransactionsApplied = true;
+    Mutex::Autolock lock(mLocalSyncPointMutex);
+
+    for (auto& point : mLocalSyncPoints) {
+        if (point->getFrameNumber() > headFrameNumber) {
+            break;
+        }
+        matchingFramesFound = true;
+
+        if (!point->frameIsAvailable()) {
+           // We haven't notified the remote layer that the frame for
+           // this point is available yet. Notify it now, and then
+           // abort this attempt to latch.
+           point->setFrameAvailable();
+           allTransactionsApplied = false;
+           break;
+        }
+
+        allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied();
+    }
+    return !matchingFramesFound || allTransactionsApplied;
+}
+
+Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime)
 {
     ATRACE_CALL();
 
@@ -1818,338 +2128,200 @@
         recomputeVisibleRegions = true;
 
         const State& s(getDrawingState());
-        return s.active.transform.transform(Region(Rect(s.active.w, s.active.h)));
+        return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
     }
 
     Region outDirtyRegion;
-    if (mQueuedFrames > 0 || mAutoRefresh) {
-
-        // if we've already called updateTexImage() without going through
-        // a composition step, we have to skip this layer at this point
-        // because we cannot call updateTeximage() without a corresponding
-        // compositionComplete() call.
-        // we'll trigger an update in onPreComposition().
-        if (mRefreshPending) {
-            return outDirtyRegion;
-        }
-
-        // If the head buffer's acquire fence hasn't signaled yet, return and
-        // try again later
-        if (!headFenceHasSignaled()) {
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        }
-
-        // Capture the old state of the layer for comparisons later
-        const State& s(getDrawingState());
-        const bool oldOpacity = isOpaque(s);
-        sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
-
-        struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
-            Layer::State& front;
-            Layer::State& current;
-            bool& recomputeVisibleRegions;
-            bool stickyTransformSet;
-            const char* name;
-            int32_t overrideScalingMode;
-            bool& freezePositionUpdates;
-
-            Reject(Layer::State& front, Layer::State& current,
-                    bool& recomputeVisibleRegions, bool stickySet,
-                    const char* name,
-                    int32_t overrideScalingMode,
-                    bool& freezePositionUpdates)
-                : front(front), current(current),
-                  recomputeVisibleRegions(recomputeVisibleRegions),
-                  stickyTransformSet(stickySet),
-                  name(name),
-                  overrideScalingMode(overrideScalingMode),
-                  freezePositionUpdates(freezePositionUpdates) {
-            }
-
-            virtual bool reject(const sp<GraphicBuffer>& buf,
-                    const BufferItem& item) {
-                if (buf == NULL) {
-                    return false;
-                }
-
-                uint32_t bufWidth  = buf->getWidth();
-                uint32_t bufHeight = buf->getHeight();
-
-                // check that we received a buffer of the right size
-                // (Take the buffer's orientation into account)
-                if (item.mTransform & Transform::ROT_90) {
-                    swap(bufWidth, bufHeight);
-                }
-
-                int actualScalingMode = overrideScalingMode >= 0 ?
-                        overrideScalingMode : item.mScalingMode;
-                bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-                if (front.active != front.requested) {
-
-                    if (isFixedSize ||
-                            (bufWidth == front.requested.w &&
-                             bufHeight == front.requested.h))
-                    {
-                        // Here we pretend the transaction happened by updating the
-                        // current and drawing states. Drawing state is only accessed
-                        // in this thread, no need to have it locked
-                        front.active = front.requested;
-
-                        // We also need to update the current state so that
-                        // we don't end-up overwriting the drawing state with
-                        // this stale current state during the next transaction
-                        //
-                        // NOTE: We don't need to hold the transaction lock here
-                        // because State::active is only accessed from this thread.
-                        current.active = front.active;
-                        current.modified = true;
-
-                        // recompute visible region
-                        recomputeVisibleRegions = true;
-                    }
-
-                    ALOGD_IF(DEBUG_RESIZE,
-                            "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                            "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
-                            "            requested={ wh={%4u,%4u} }}\n",
-                            name,
-                            bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                            front.active.w, front.active.h,
-                            front.crop.left,
-                            front.crop.top,
-                            front.crop.right,
-                            front.crop.bottom,
-                            front.crop.getWidth(),
-                            front.crop.getHeight(),
-                            front.requested.w, front.requested.h);
-                }
-
-                if (!isFixedSize && !stickyTransformSet) {
-                    if (front.active.w != bufWidth ||
-                        front.active.h != bufHeight) {
-                        // reject this buffer
-                        ALOGE("[%s] rejecting buffer: "
-                                "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
-                                name, bufWidth, bufHeight, front.active.w, front.active.h);
-                        return true;
-                    }
-                }
-
-                // if the transparent region has changed (this test is
-                // conservative, but that's fine, worst case we're doing
-                // a bit of extra work), we latch the new one and we
-                // trigger a visible-region recompute.
-                if (!front.activeTransparentRegion.isTriviallyEqual(
-                        front.requestedTransparentRegion)) {
-                    front.activeTransparentRegion = front.requestedTransparentRegion;
-
-                    // We also need to update the current state so that
-                    // we don't end-up overwriting the drawing state with
-                    // this stale current state during the next transaction
-                    //
-                    // NOTE: We don't need to hold the transaction lock here
-                    // because State::active is only accessed from this thread.
-                    current.activeTransparentRegion = front.activeTransparentRegion;
-
-                    // recompute visible region
-                    recomputeVisibleRegions = true;
-                }
-
-                if (front.crop != front.requestedCrop) {
-                    front.crop = front.requestedCrop;
-                    current.crop = front.requestedCrop;
-                    recomputeVisibleRegions = true;
-                }
-                freezePositionUpdates = false;
-
-                return false;
-            }
-        };
-
-        Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                getProducerStickyTransform() != 0, mName.string(),
-                mOverrideScalingMode, mFreezePositionUpdates);
-
-
-        // Check all of our local sync points to ensure that all transactions
-        // which need to have been applied prior to the frame which is about to
-        // be latched have signaled
-
-        auto headFrameNumber = getHeadFrameNumber();
-        bool matchingFramesFound = false;
-        bool allTransactionsApplied = true;
-        {
-            Mutex::Autolock lock(mLocalSyncPointMutex);
-            for (auto& point : mLocalSyncPoints) {
-                if (point->getFrameNumber() > headFrameNumber) {
-                    break;
-                }
-
-                matchingFramesFound = true;
-
-                if (!point->frameIsAvailable()) {
-                    // We haven't notified the remote layer that the frame for
-                    // this point is available yet. Notify it now, and then
-                    // abort this attempt to latch.
-                    point->setFrameAvailable();
-                    allTransactionsApplied = false;
-                    break;
-                }
-
-                allTransactionsApplied &= point->transactionIsApplied();
-            }
-        }
-
-        if (matchingFramesFound && !allTransactionsApplied) {
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        }
-
-        // This boolean is used to make sure that SurfaceFlinger's shadow copy
-        // of the buffer queue isn't modified when the buffer queue is returning
-        // BufferItem's that weren't actually queued. This can happen in shared
-        // buffer mode.
-        bool queuedBuffer = false;
-        status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
-                mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
-                mLastFrameNumberReceived);
-        if (updateResult == BufferQueue::PRESENT_LATER) {
-            // Producer doesn't want buffer to be displayed yet.  Signal a
-            // layer update so we check again at the next opportunity.
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
-            // If the buffer has been rejected, remove it from the shadow queue
-            // and return early
-            if (queuedBuffer) {
-                Mutex::Autolock lock(mQueueItemLock);
-                mQueueItems.removeAt(0);
-                android_atomic_dec(&mQueuedFrames);
-            }
-            return outDirtyRegion;
-        } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
-            // This can occur if something goes wrong when trying to create the
-            // EGLImage for this buffer. If this happens, the buffer has already
-            // been released, so we need to clean up the queue and bug out
-            // early.
-            if (queuedBuffer) {
-                Mutex::Autolock lock(mQueueItemLock);
-                mQueueItems.clear();
-                android_atomic_and(0, &mQueuedFrames);
-            }
-
-            // Once we have hit this state, the shadow queue may no longer
-            // correctly reflect the incoming BufferQueue's contents, so even if
-            // updateTexImage starts working, the only safe course of action is
-            // to continue to ignore updates.
-            mUpdateTexImageFailed = true;
-
-            return outDirtyRegion;
-        }
-
-        if (queuedBuffer) {
-            // Autolock scope
-            auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
-            Mutex::Autolock lock(mQueueItemLock);
-
-            // Remove any stale buffers that have been dropped during
-            // updateTexImage
-            while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-                mQueueItems.removeAt(0);
-                android_atomic_dec(&mQueuedFrames);
-            }
-
-            mQueueItems.removeAt(0);
-        }
-
-
-        // Decrement the queued-frames count.  Signal another event if we
-        // have more frames pending.
-        if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
-                || mAutoRefresh) {
-            mFlinger->signalLayerUpdate();
-        }
-
-        if (updateResult != NO_ERROR) {
-            // something happened!
-            recomputeVisibleRegions = true;
-            return outDirtyRegion;
-        }
-
-        // update the active buffer
-        mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
-        if (mActiveBuffer == NULL) {
-            // this can only happen if the very first buffer was rejected.
-            return outDirtyRegion;
-        }
-
-        mRefreshPending = true;
-        mFrameLatencyNeeded = true;
-        if (oldActiveBuffer == NULL) {
-             // the first time we receive a buffer, we need to trigger a
-             // geometry invalidation.
-            recomputeVisibleRegions = true;
-         }
-
-        Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
-        const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
-        const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
-        if ((crop != mCurrentCrop) ||
-            (transform != mCurrentTransform) ||
-            (scalingMode != mCurrentScalingMode))
-        {
-            mCurrentCrop = crop;
-            mCurrentTransform = transform;
-            mCurrentScalingMode = scalingMode;
-            recomputeVisibleRegions = true;
-        }
-
-        if (oldActiveBuffer != NULL) {
-            uint32_t bufWidth  = mActiveBuffer->getWidth();
-            uint32_t bufHeight = mActiveBuffer->getHeight();
-            if (bufWidth != uint32_t(oldActiveBuffer->width) ||
-                bufHeight != uint32_t(oldActiveBuffer->height)) {
-                recomputeVisibleRegions = true;
-            }
-        }
-
-        mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
-        if (oldOpacity != isOpaque(s)) {
-            recomputeVisibleRegions = true;
-        }
-
-        mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
-        // Remove any sync points corresponding to the buffer which was just
-        // latched
-        {
-            Mutex::Autolock lock(mLocalSyncPointMutex);
-            auto point = mLocalSyncPoints.begin();
-            while (point != mLocalSyncPoints.end()) {
-                if (!(*point)->frameIsAvailable() ||
-                        !(*point)->transactionIsApplied()) {
-                    // This sync point must have been added since we started
-                    // latching. Don't drop it yet.
-                    ++point;
-                    continue;
-                }
-
-                if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
-                    point = mLocalSyncPoints.erase(point);
-                } else {
-                    ++point;
-                }
-            }
-        }
-
-        // FIXME: postedRegion should be dirty & bounds
-        Region dirtyRegion(Rect(s.active.w, s.active.h));
-
-        // transform the dirty region to window-manager space
-        outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+    if (mQueuedFrames <= 0 && !mAutoRefresh) {
+        return outDirtyRegion;
     }
+
+    // if we've already called updateTexImage() without going through
+    // a composition step, we have to skip this layer at this point
+    // because we cannot call updateTeximage() without a corresponding
+    // compositionComplete() call.
+    // we'll trigger an update in onPreComposition().
+    if (mRefreshPending) {
+        return outDirtyRegion;
+    }
+
+    // If the head buffer's acquire fence hasn't signaled yet, return and
+    // try again later
+    if (!headFenceHasSignaled()) {
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    }
+
+    // Capture the old state of the layer for comparisons later
+    const State& s(getDrawingState());
+    const bool oldOpacity = isOpaque(s);
+    sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
+
+    if (!allTransactionsSignaled()) {
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    }
+
+    // This boolean is used to make sure that SurfaceFlinger's shadow copy
+    // of the buffer queue isn't modified when the buffer queue is returning
+    // BufferItem's that weren't actually queued. This can happen in shared
+    // buffer mode.
+    bool queuedBuffer = false;
+    LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+                    getProducerStickyTransform() != 0, mName.string(),
+                    mOverrideScalingMode, mFreezeGeometryUpdates);
+    status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
+            mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
+            mLastFrameNumberReceived);
+    if (updateResult == BufferQueue::PRESENT_LATER) {
+        // Producer doesn't want buffer to be displayed yet.  Signal a
+        // layer update so we check again at the next opportunity.
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
+        // If the buffer has been rejected, remove it from the shadow queue
+        // and return early
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mQueueItems.removeAt(0);
+            android_atomic_dec(&mQueuedFrames);
+        }
+        return outDirtyRegion;
+    } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+        // This can occur if something goes wrong when trying to create the
+        // EGLImage for this buffer. If this happens, the buffer has already
+        // been released, so we need to clean up the queue and bug out
+        // early.
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mQueueItems.clear();
+            android_atomic_and(0, &mQueuedFrames);
+        }
+
+        // Once we have hit this state, the shadow queue may no longer
+        // correctly reflect the incoming BufferQueue's contents, so even if
+        // updateTexImage starts working, the only safe course of action is
+        // to continue to ignore updates.
+        mUpdateTexImageFailed = true;
+
+        return outDirtyRegion;
+    }
+
+    if (queuedBuffer) {
+        // Autolock scope
+        auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+        Mutex::Autolock lock(mQueueItemLock);
+
+        // Remove any stale buffers that have been dropped during
+        // updateTexImage
+        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+            mQueueItems.removeAt(0);
+            android_atomic_dec(&mQueuedFrames);
+        }
+
+        mQueueItems.removeAt(0);
+    }
+
+
+    // Decrement the queued-frames count.  Signal another event if we
+    // have more frames pending.
+    if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
+            || mAutoRefresh) {
+        mFlinger->signalLayerUpdate();
+    }
+
+    // update the active buffer
+    mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer(
+            &mActiveBufferSlot);
+    if (mActiveBuffer == NULL) {
+        // this can only happen if the very first buffer was rejected.
+        return outDirtyRegion;
+    }
+
+    mBufferLatched = true;
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+#ifndef USE_HWC2
+        auto releaseFenceTime = std::make_shared<FenceTime>(
+                mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+        mReleaseTimeline.push(releaseFenceTime);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRelease(mPreviousFrameNumber,
+                    latchTime, std::move(releaseFenceTime));
+        }
+#endif
+    }
+
+    mRefreshPending = true;
+    mFrameLatencyNeeded = true;
+    if (oldActiveBuffer == NULL) {
+         // the first time we receive a buffer, we need to trigger a
+         // geometry invalidation.
+        recomputeVisibleRegions = true;
+     }
+
+    setDataSpace(mSurfaceFlingerConsumer->getCurrentDataSpace());
+
+    Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+    const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+    const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
+    if ((crop != mCurrentCrop) ||
+        (transform != mCurrentTransform) ||
+        (scalingMode != mCurrentScalingMode))
+    {
+        mCurrentCrop = crop;
+        mCurrentTransform = transform;
+        mCurrentScalingMode = scalingMode;
+        recomputeVisibleRegions = true;
+    }
+
+    if (oldActiveBuffer != NULL) {
+        uint32_t bufWidth  = mActiveBuffer->getWidth();
+        uint32_t bufHeight = mActiveBuffer->getHeight();
+        if (bufWidth != uint32_t(oldActiveBuffer->width) ||
+            bufHeight != uint32_t(oldActiveBuffer->height)) {
+            recomputeVisibleRegions = true;
+        }
+    }
+
+    mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+    if (oldOpacity != isOpaque(s)) {
+        recomputeVisibleRegions = true;
+    }
+
+    // Remove any sync points corresponding to the buffer which was just
+    // latched
+    {
+        Mutex::Autolock lock(mLocalSyncPointMutex);
+        auto point = mLocalSyncPoints.begin();
+        while (point != mLocalSyncPoints.end()) {
+            if (!(*point)->frameIsAvailable() ||
+                    !(*point)->transactionIsApplied()) {
+                // This sync point must have been added since we started
+                // latching. Don't drop it yet.
+                ++point;
+                continue;
+            }
+
+            if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
+                point = mLocalSyncPoints.erase(point);
+            } else {
+                ++point;
+            }
+        }
+    }
+
+    // FIXME: postedRegion should be dirty & bounds
+    Region dirtyRegion(Rect(s.active.w, s.active.h));
+
+    // transform the dirty region to window-manager space
+    outDirtyRegion = (getTransform().transform(dirtyRegion));
+
     return outDirtyRegion;
 }
 
@@ -2211,7 +2383,9 @@
             "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
 #endif
             "      client=%p\n",
-            s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
+            getLayerStack(), s.z,
+            s.active.transform.tx(), s.active.transform.ty(),
+            s.active.w, s.active.h,
             s.crop.left, s.crop.top,
             s.crop.right, s.crop.bottom,
             s.finalCrop.left, s.finalCrop.top,
@@ -2238,7 +2412,7 @@
             mQueuedFrames, mRefreshPending);
 
     if (mSurfaceFlingerConsumer != 0) {
-        mSurfaceFlingerConsumer->dump(result, "            ");
+        mSurfaceFlingerConsumer->dumpState(result, "            ");
     }
 }
 
@@ -2306,22 +2480,30 @@
     mFrameTracker.getStats(outStats);
 }
 
-void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber,
-        bool* outIsGlesComposition, nsecs_t* outPostedTime,
-        sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const {
-    *outName = mName;
-    *outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+void Layer::dumpFrameEvents(String8& result) {
+    result.appendFormat("- Layer %s (%s, %p)\n",
+            getName().string(), getTypeId(), this);
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mFrameEventHistory.checkFencesForCompletion();
+    mFrameEventHistory.dump(result);
+}
 
-#ifdef USE_HWC2
-    *outIsGlesComposition = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
-            mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
-            HWC2::Composition::Client : true;
-#else
-    *outIsGlesComposition = mIsGlesComposition;
-#endif
-    *outPostedTime = mSurfaceFlingerConsumer->getTimestamp();
-    *outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
-    *outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
+void Layer::onDisconnect() {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mFrameEventHistory.onDisconnect();
+}
+
+void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta *outDelta) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (newTimestamps) {
+        mAcquireTimeline.push(newTimestamps->acquireFence);
+        mFrameEventHistory.addQueue(*newTimestamps);
+    }
+
+    if (outDelta) {
+        mFrameEventHistory.getAndResetDelta(outDelta);
+    }
 }
 
 std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
@@ -2341,19 +2523,216 @@
     return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
 }
 
-// ---------------------------------------------------------------------------
-
-Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
-        const sp<Layer>& layer)
-    : mFlinger(flinger), mLayer(layer) {
+size_t Layer::getChildrenCount() const {
+    size_t count = 0;
+    for (const sp<Layer>& child : mCurrentChildren) {
+        count += 1 + child->getChildrenCount();
+    }
+    return count;
 }
 
-Layer::LayerCleaner::~LayerCleaner() {
-    // destroy client resources
-    mFlinger->onLayerDestroyed(mLayer);
+void Layer::addChild(const sp<Layer>& layer) {
+    mCurrentChildren.add(layer);
+    layer->setParent(this);
+}
+
+ssize_t Layer::removeChild(const sp<Layer>& layer) {
+    layer->setParent(nullptr);
+    return mCurrentChildren.remove(layer);
+}
+
+bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
+    sp<Handle> handle = nullptr;
+    sp<Layer> newParent = nullptr;
+    if (newParentHandle == nullptr) {
+        return false;
+    }
+    handle = static_cast<Handle*>(newParentHandle.get());
+    newParent = handle->owner.promote();
+    if (newParent == nullptr) {
+        ALOGE("Unable to promote Layer handle");
+        return false;
+    }
+
+    for (const sp<Layer>& child : mCurrentChildren) {
+        newParent->addChild(child);
+
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr) {
+            client->setParentLayer(newParent);
+        }
+    }
+    mCurrentChildren.clear();
+
+    return true;
+}
+
+bool Layer::detachChildren() {
+    traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* child) {
+        if (child == this) {
+            return;
+        }
+
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr) {
+            client->detachLayer(child);
+        }
+    });
+
+    return true;
+}
+
+void Layer::setParent(const sp<Layer>& layer) {
+    mCurrentParent = layer;
+}
+
+void Layer::clearSyncPoints() {
+    for (const auto& child : mCurrentChildren) {
+        child->clearSyncPoints();
+    }
+
+    Mutex::Autolock lock(mLocalSyncPointMutex);
+    for (auto& point : mLocalSyncPoints) {
+        point->setFrameAvailable();
+    }
+    mLocalSyncPoints.clear();
+}
+
+int32_t Layer::getZ() const {
+    return mDrawingState.z;
+}
+
+LayerVector Layer::makeTraversalList(LayerVector::StateSet stateSet) {
+    LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
+                        "makeTraversalList received invalid stateSet");
+    const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+    const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
+    const State& state = useDrawing ? mDrawingState : mCurrentState;
+
+    if (state.zOrderRelatives.size() == 0) {
+        return children;
+    }
+    LayerVector traverse;
+
+    for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+        sp<Layer> strongRelative = weakRelative.promote();
+        if (strongRelative != nullptr) {
+            traverse.add(strongRelative);
+        }
+    }
+
+    for (const sp<Layer>& child : children) {
+        traverse.add(child);
+    }
+
+    return traverse;
+}
+
+/**
+ * Negatively signed relatives are before 'this' in Z-order.
+ */
+void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) {
+    LayerVector list = makeTraversalList(stateSet);
+
+    size_t i = 0;
+    for (; i < list.size(); i++) {
+        const auto& relative = list[i];
+        if (relative->getZ() >= 0) {
+            break;
+        }
+        relative->traverseInZOrder(stateSet, visitor);
+    }
+    visitor(this);
+    for (; i < list.size(); i++) {
+        const auto& relative = list[i];
+        relative->traverseInZOrder(stateSet, visitor);
+    }
+}
+
+/**
+ * Positively signed relatives are before 'this' in reverse Z-order.
+ */
+void Layer::traverseInReverseZOrder(LayerVector::StateSet stateSet,
+                                    const LayerVector::Visitor& visitor) {
+    LayerVector list = makeTraversalList(stateSet);
+
+    int32_t i = 0;
+    for (i = list.size()-1; i>=0; i--) {
+        const auto& relative = list[i];
+        if (relative->getZ() < 0) {
+            break;
+        }
+        relative->traverseInReverseZOrder(stateSet, visitor);
+    }
+    visitor(this);
+    for (; i>=0; i--) {
+        const auto& relative = list[i];
+        relative->traverseInReverseZOrder(stateSet, visitor);
+    }
+}
+
+Transform Layer::getTransform() const {
+    Transform t;
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        t = p->getTransform();
+
+        // If the parent is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
+        // it isFixedSize) then there may be additional scaling not accounted
+        // for in the transform. We need to mirror this scaling in child surfaces
+        // or we will break the contract where WM can treat child surfaces as
+        // pixels in the parent surface.
+        if (p->isFixedSize()) {
+            int bufferWidth;
+            int bufferHeight;
+            if ((p->mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
+                bufferWidth = p->mActiveBuffer->getWidth();
+                bufferHeight = p->mActiveBuffer->getHeight();
+            } else {
+                bufferHeight = p->mActiveBuffer->getWidth();
+                bufferWidth = p->mActiveBuffer->getHeight();
+            }
+            float sx = p->getDrawingState().active.w /
+                    static_cast<float>(bufferWidth);
+            float sy = p->getDrawingState().active.h /
+                    static_cast<float>(bufferHeight);
+            Transform extraParentScaling;
+            extraParentScaling.set(sx, 0, 0, sy);
+            t = t * extraParentScaling;
+        }
+    }
+    return t * getDrawingState().active.transform;
+}
+
+#ifdef USE_HWC2
+float Layer::getAlpha() const {
+    const auto& p = mDrawingParent.promote();
+
+    float parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0;
+    return parentAlpha * getDrawingState().alpha;
+}
+#else
+uint8_t Layer::getAlpha() const {
+    const auto& p = mDrawingParent.promote();
+
+    float parentAlpha = (p != nullptr) ? (p->getAlpha() / 255.0f) : 1.0;
+    float drawingAlpha = getDrawingState().alpha / 255.0f;
+    drawingAlpha = drawingAlpha * parentAlpha;
+    return static_cast<uint8_t>(std::round(drawingAlpha * 255));
+}
+#endif
+
+void Layer::commitChildList() {
+    for (size_t i = 0; i < mCurrentChildren.size(); i++) {
+        const auto& child = mCurrentChildren[i];
+        child->commitChildList();
+    }
+    mDrawingChildren = mCurrentChildren;
+    mDrawingParent = mCurrentParent;
 }
 
 // ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2ce1340..24fc10d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -40,13 +40,14 @@
 
 #include "FrameTracker.h"
 #include "Client.h"
+#include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerConsumer.h"
 #include "Transform.h"
 
 #include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/FloatRect.h"
+#include "DisplayHardware/HWComposerBufferCache.h"
 #include "RenderEngine/Mesh.h"
 #include "RenderEngine/Texture.h"
 
@@ -108,8 +109,15 @@
     struct State {
         Geometry active;
         Geometry requested;
-        uint32_t z;
+        int32_t z;
+
+        // The identifier of the layer stack this layer belongs to. A layer can
+        // only be associated to a single layer stack. A layer stack is a
+        // z-ordered group of layers which can be associated to one or more
+        // displays. Using the same layer stack on different displays is a way
+        // to achieve mirroring.
         uint32_t layerStack;
+
 #ifdef USE_HWC2
         float alpha;
 #else
@@ -121,14 +129,17 @@
         int32_t sequence; // changes when visible regions can change
         bool modified;
 
+        // Crop is expressed in layer space coordinate.
         Rect crop;
         Rect requestedCrop;
 
+        // finalCrop is expressed in display space coordinate.
         Rect finalCrop;
+        Rect requestedFinalCrop;
 
-        // If set, defers this state update until the Layer identified by handle
+        // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
-        wp<IBinder> handle;
+        wp<Layer> barrierLayer;
         uint64_t frameNumber;
 
         // the transparentRegion hint is a bit special, it's latched only
@@ -136,6 +147,16 @@
         // dependent.
         Region activeTransparentRegion;
         Region requestedTransparentRegion;
+        android_dataspace dataSpace;
+
+        uint32_t appId;
+        uint32_t type;
+
+        // If non-null, a Surface this Surface's Z-order is interpreted relative to.
+        wp<Layer> zOrderRelativeOf;
+
+        // A list of surfaces whose Z-order is interpreted relative to ours.
+        SortedVector<wp<Layer>> zOrderRelatives;
     };
 
     // -----------------------------------------------------------------------
@@ -148,23 +169,75 @@
     // the this layer's size and format
     status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
 
-    // modify current state
-    bool setPosition(float x, float y, bool immediate);
-    bool setLayer(uint32_t z);
+    // ------------------------------------------------------------------------
+    // Geometry setting functions.
+    //
+    // The following group of functions are used to specify the layers
+    // bounds, and the mapping of the texture on to those bounds. According
+    // to various settings changes to them may apply immediately, or be delayed until
+    // a pending resize is completed by the producer submitting a buffer. For example
+    // if we were to change the buffer size, and update the matrix ahead of the
+    // new buffer arriving, then we would be stretching the buffer to a different
+    // aspect before and after the buffer arriving, which probably isn't what we wanted.
+    //
+    // The first set of geometry functions are controlled by the scaling mode, described
+    // in window.h. The scaling mode may be set by the client, as it submits buffers.
+    // This value may be overriden through SurfaceControl, with setOverrideScalingMode.
+    //
+    // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
+    // matrix updates will not be applied while a resize is pending
+    // and the size and transform will remain in their previous state
+    // until a new buffer is submitted. If the scaling mode is another value
+    // then the old-buffer will immediately be scaled to the pending size
+    // and the new matrix will be immediately applied following this scaling
+    // transformation.
+
+    // Set the default buffer size for the assosciated Producer, in pixels. This is
+    // also the rendered size of the layer prior to any transformations. Parent
+    // or local matrix transformations will not affect the size of the buffer,
+    // but may affect it's on-screen size or clipping.
     bool setSize(uint32_t w, uint32_t h);
+    // Set a 2x2 transformation matrix on the layer. This transform
+    // will be applied after parent transforms, but before any final
+    // producer specified transform.
+    bool setMatrix(const layer_state_t::matrix22_t& matrix);
+
+    // This second set of geometry attributes are controlled by
+    // setGeometryAppliesWithResize, and their default mode is to be
+    // immediate. If setGeometryAppliesWithResize is specified
+    // while a resize is pending, then update of these attributes will
+    // be delayed until the resize completes.
+
+    // setPosition operates in parent buffer space (pre parent-transform) or display
+    // space for top-level layers.
+    bool setPosition(float x, float y, bool immediate);
+    // Buffer space
+    bool setCrop(const Rect& crop, bool immediate);
+    // Parent buffer space/display space
+    bool setFinalCrop(const Rect& crop, bool immediate);
+
+    // TODO(b/38182121): Could we eliminate the various latching modes by
+    // using the layer hierarchy?
+    // -----------------------------------------------------------------------
+    bool setLayer(int32_t z);
+    bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
+
 #ifdef USE_HWC2
     bool setAlpha(float alpha);
 #else
     bool setAlpha(uint8_t alpha);
 #endif
-    bool setMatrix(const layer_state_t::matrix22_t& matrix);
     bool setTransparentRegionHint(const Region& transparent);
     bool setFlags(uint8_t flags, uint8_t mask);
-    bool setCrop(const Rect& crop, bool immediate);
-    bool setFinalCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
-    void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+    bool setDataSpace(android_dataspace dataSpace);
+    uint32_t getLayerStack() const;
+    void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
+    void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     bool setOverrideScalingMode(int32_t overrideScalingMode);
+    void setInfo(uint32_t type, uint32_t appId);
+    bool reparentChildren(const sp<IBinder>& layer);
+    bool detachChildren();
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -180,11 +253,6 @@
     Rect computeBounds(const Region& activeTransparentRegion) const;
     Rect computeBounds() const;
 
-    class Handle;
-    sp<IBinder> getHandle();
-    sp<IGraphicBufferProducer> getProducer() const;
-    const String8& getName() const;
-
     int32_t getSequence() const { return sequence; }
 
     // -----------------------------------------------------------------------
@@ -219,6 +287,14 @@
     virtual bool isVisible() const;
 
     /*
+     * isHiddenByPolicy - true if this layer has been forced invisible.
+     * just because this is false, doesn't mean isVisible() is true.
+     * For example if this layer has no active buffer, it may not be hidden by
+     * policy, but it still can not be visible.
+     */
+    virtual bool isHiddenByPolicy() const;
+
+    /*
      * isFixedSize - true if content has a fixed size
      */
     virtual bool isFixedSize() const;
@@ -234,10 +310,12 @@
     // -----------------------------------------------------------------------
 
 #ifdef USE_HWC2
-    void setGeometry(const sp<const DisplayDevice>& displayDevice);
+    void setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z);
     void forceClientComposition(int32_t hwcId);
     void setPerFrameData(const sp<const DisplayDevice>& displayDevice);
 
+    android_dataspace getDataSpace() const;
+
     // callIntoHwc exists so we can update our local state and call
     // acceptDisplayChanges without unnecessarily updating the device's state
     void setCompositionType(int32_t hwcId, HWC2::Composition type,
@@ -275,17 +353,19 @@
      * called before composition.
      * returns true if the layer has pending updates.
      */
-    bool onPreComposition();
+    bool onPreComposition(nsecs_t refreshStartTime);
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    bool onPostComposition();
+    bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
+            const std::shared_ptr<FenceTime>& presentFence,
+            const CompositorTiming& compositorTiming);
 
 #ifdef USE_HWC2
     // If a buffer was replaced this frame, release the former buffer
-    void releasePendingBuffer();
+    void releasePendingBuffer(nsecs_t dequeueReadyTime);
 #endif
 
     /*
@@ -328,7 +408,7 @@
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    Region latchBuffer(bool& recomputeVisibleRegions);
+    Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime);
 
     bool isPotentialCursor() const { return mPotentialCursor;}
 
@@ -385,10 +465,14 @@
         }
     }
 
+    void clearHwcLayers() {
+        mHwcLayers.clear();
+    }
+
 #endif
     // -----------------------------------------------------------------------
 
-    void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const;
+    void clearWithOpenGL(const sp<const DisplayDevice>& hw) const;
     void setFiltering(bool filtering);
     bool getFiltering() const;
 
@@ -407,29 +491,53 @@
     void miniDump(String8& result, int32_t hwcId) const;
 #endif
     void dumpFrameStats(String8& result) const;
+    void dumpFrameEvents(String8& result);
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
 
-    void getFenceData(String8* outName, uint64_t* outFrameNumber,
-            bool* outIsGlesComposition, nsecs_t* outPostedTime,
-            sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
-
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
 
-    bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const {
-        return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
-    }
+    void onDisconnect();
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
+            FrameEventHistoryDelta* outDelta);
 
     bool getTransformToDisplayInverse() const;
 
+    Transform getTransform() const;
+
+    // Returns the Alpha of the Surface, accounting for the Alpha
+    // of parent Surfaces in the hierarchy (alpha's will be multiplied
+    // down the hierarchy).
+#ifdef USE_HWC2
+    float getAlpha() const;
+#else
+    uint8_t getAlpha() const;
+#endif
+
+    void traverseInReverseZOrder(LayerVector::StateSet stateSet,
+                                 const LayerVector::Visitor& visitor);
+    void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+
+    size_t getChildrenCount() const;
+    void addChild(const sp<Layer>& layer);
+    // Returns index if removed, or negative value otherwise
+    // for symmetry with Vector::remove
+    ssize_t removeChild(const sp<Layer>& layer);
+    sp<Layer> getParent() const { return mCurrentParent.promote(); }
+    bool hasParent() const { return getParent() != nullptr; }
+
+    Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+    bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
+
+    // Copy the current list of children to the drawing state. Called by
+    // SurfaceFlinger to complete a transaction.
+    void commitChildList();
+
+    int32_t getZ() const;
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
-
-    virtual void onFirstRef();
-
     /*
      * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
      * is called.
@@ -438,13 +546,24 @@
         sp<SurfaceFlinger> mFlinger;
         wp<Layer> mLayer;
     protected:
-        ~LayerCleaner();
+        ~LayerCleaner() {
+            // destroy client resources
+            mFlinger->onLayerDestroyed(mLayer);
+        }
     public:
-        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer);
+        LayerCleaner(const sp<SurfaceFlinger>& flinger,
+                const sp<Layer>& layer)
+            : mFlinger(flinger), mLayer(layer) {
+        }
     };
 
 
+    virtual void onFirstRef();
+
+
+
 private:
+    friend class SurfaceInterceptor;
     // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
     virtual void onFrameAvailable(const BufferItem& item) override;
     virtual void onFrameReplaced(const BufferItem& item) override;
@@ -456,14 +575,19 @@
     bool needsFiltering(const sp<const DisplayDevice>& hw) const;
 
     uint32_t getEffectiveUsage(uint32_t usage) const;
+
     FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
+    // Compute the initial crop as specified by parent layers and the SurfaceControl
+    // for this layer. Does not include buffer crop from the IGraphicBufferProducer
+    // client, as that should not affect child clipping. Returns in screen space.
+    Rect computeInitialCrop(const sp<const DisplayDevice>& hw) const;
     bool isCropped() const;
     static bool getOpacityForFormat(uint32_t format);
 
     // drawing
-    void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+    void clearWithOpenGL(const sp<const DisplayDevice>& hw,
             float r, float g, float b, float alpha) const;
-    void drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+    void drawWithOpenGL(const sp<const DisplayDevice>& hw,
             bool useIdentityTransform) const;
 
     // Temporary - Used only for LEGACY camera mode.
@@ -472,12 +596,18 @@
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
+    void setParent(const sp<Layer>& layer);
+
+    LayerVector makeTraversalList(LayerVector::StateSet stateSet);
+    void addZOrderRelative(const wp<Layer>& relative);
+    void removeZOrderRelative(const wp<Layer>& relative);
+
     // -----------------------------------------------------------------------
 
     class SyncPoint
     {
     public:
-        SyncPoint(uint64_t frameNumber) : mFrameNumber(frameNumber),
+        explicit SyncPoint(uint64_t frameNumber) : mFrameNumber(frameNumber),
                 mFrameIsAvailable(false), mTransactionIsApplied(false) {}
 
         uint64_t getFrameNumber() const {
@@ -526,16 +656,42 @@
     void popPendingState(State* stateToCommit);
     bool applyPendingStates(State* stateToCommit);
 
+    void clearSyncPoints();
+
     // Returns mCurrentScaling mode (originating from the
     // Client) or mOverrideScalingMode mode (originating from
     // the Surface Controller) if set.
     uint32_t getEffectiveScalingMode() const;
 public:
+    /*
+     * The layer handle is just a BBinder object passed to the client
+     * (remote process) -- we don't keep any reference on our side such that
+     * the dtor is called when the remote side let go of its reference.
+     *
+     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
+     * this layer when the handle is destroyed.
+     */
+    class Handle : public BBinder, public LayerCleaner {
+        public:
+            Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+                : LayerCleaner(flinger, layer), owner(layer) {}
+
+            wp<Layer> owner;
+    };
+
+    sp<IBinder> getHandle();
+    sp<IGraphicBufferProducer> getProducer() const;
+    const String8& getName() const;
     void notifyAvailableFrames();
 private:
 
     // -----------------------------------------------------------------------
 
+    // Check all of the local sync points to ensure that all transactions
+    // which need to have been applied prior to the frame which is about to
+    // be latched have signaled
+    bool allTransactionsSignaled();
+
     // constants
     sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
     sp<IGraphicBufferProducer> mProducer;
@@ -556,9 +712,19 @@
     // thread-safe
     volatile int32_t mQueuedFrames;
     volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
+
+    // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
 
+    // Timestamp history for the consumer to query.
+    // Accessed by both consumer and producer on main and binder threads.
+    Mutex mFrameEventHistoryMutex;
+    ConsumerFrameEventHistory mFrameEventHistory;
+    FenceTimeline mAcquireTimeline;
+    FenceTimeline mReleaseTimeline;
+
     // main thread
+    int mActiveBufferSlot;
     sp<GraphicBuffer> mActiveBuffer;
     sp<NativeHandle> mSidebandStream;
     Rect mCurrentCrop;
@@ -567,7 +733,9 @@
     // We encode unset as -1.
     int32_t mOverrideScalingMode;
     bool mCurrentOpacity;
+    bool mBufferLatched = false;  // TODO: Use mActiveBuffer?
     std::atomic<uint64_t> mCurrentFrameNumber;
+    uint64_t mPreviousFrameNumber; // Only accessed on the main thread.
     bool mRefreshPending;
     bool mFrameLatencyNeeded;
     // Whether filtering is forced on or not
@@ -594,7 +762,13 @@
         bool clearClientTarget;
         Rect displayFrame;
         FloatRect sourceCrop;
+        HWComposerBufferCache bufferCache;
     };
+
+    // A layer can be attached to multiple displays when operating in mirror mode
+    // (a.k.a: when several displays are attached with equal layerStack). In this
+    // case we need to keep track. In non-mirror mode, a layer will have only one
+    // HWCInfo. This map key is a display layerStack.
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
 #else
     bool mIsGlesComposition;
@@ -617,10 +791,18 @@
     Condition mQueueItemCondition;
     Vector<BufferItem> mQueueItems;
     std::atomic<uint64_t> mLastFrameNumberReceived;
-    bool mUpdateTexImageFailed; // This is only modified from the main thread
+    bool mUpdateTexImageFailed; // This is only accessed on the main thread.
 
     bool mAutoRefresh;
-    bool mFreezePositionUpdates;
+    bool mFreezeGeometryUpdates;
+
+    // Child list about to be committed/used for editing.
+    LayerVector mCurrentChildren;
+    // Child list used for rendering.
+    LayerVector mDrawingChildren;
+
+    wp<Layer> mCurrentParent;
+    wp<Layer> mDrawingParent;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index 4d5b3db..daebf8a 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -59,7 +59,7 @@
 
 bool LayerDim::isVisible() const {
     const Layer::State& s(getDrawingState());
-    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha;
+    return !isHiddenByPolicy() && s.alpha;
 }
 
 
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
new file mode 100644
index 0000000..6e37b45
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerRejecter.h"
+
+#include <gui/BufferItem.h>
+
+#include "clz.h"
+
+#define DEBUG_RESIZE 0
+
+namespace android {
+
+LayerRejecter::LayerRejecter(Layer::State& front,
+                             Layer::State& current,
+                             bool& recomputeVisibleRegions,
+                             bool stickySet,
+                             const char* name,
+                             int32_t overrideScalingMode,
+                             bool& freezePositionUpdates)
+  : mFront(front),
+    mCurrent(current),
+    mRecomputeVisibleRegions(recomputeVisibleRegions),
+    mStickyTransformSet(stickySet),
+    mName(name),
+    mOverrideScalingMode(overrideScalingMode),
+    mFreezeGeometryUpdates(freezePositionUpdates) {}
+
+bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
+    if (buf == NULL) {
+        return false;
+    }
+
+    uint32_t bufWidth = buf->getWidth();
+    uint32_t bufHeight = buf->getHeight();
+
+    // check that we received a buffer of the right size
+    // (Take the buffer's orientation into account)
+    if (item.mTransform & Transform::ROT_90) {
+        swap(bufWidth, bufHeight);
+    }
+
+    int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+    bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    if (mFront.active != mFront.requested) {
+        if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+            // Here we pretend the transaction happened by updating the
+            // current and drawing states. Drawing state is only accessed
+            // in this thread, no need to have it locked
+            mFront.active = mFront.requested;
+
+            // We also need to update the current state so that
+            // we don't end-up overwriting the drawing state with
+            // this stale current state during the next transaction
+            //
+            // NOTE: We don't need to hold the transaction lock here
+            // because State::active is only accessed from this thread.
+            mCurrent.active = mFront.active;
+            mCurrent.modified = true;
+
+            // recompute visible region
+            mRecomputeVisibleRegions = true;
+
+            mFreezeGeometryUpdates = false;
+
+            if (mFront.crop != mFront.requestedCrop) {
+                mFront.crop = mFront.requestedCrop;
+                mCurrent.crop = mFront.requestedCrop;
+                mRecomputeVisibleRegions = true;
+            }
+            if (mFront.finalCrop != mFront.requestedFinalCrop) {
+                mFront.finalCrop = mFront.requestedFinalCrop;
+                mCurrent.finalCrop = mFront.requestedFinalCrop;
+                mRecomputeVisibleRegions = true;
+            }
+        }
+
+        ALOGD_IF(DEBUG_RESIZE,
+                 "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
+                 "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+                 "}\n"
+                 "            requested={ wh={%4u,%4u} }}\n",
+                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
+                 mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
+                 mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
+                 mFront.requested.w, mFront.requested.h);
+    }
+
+    if (!isFixedSize && !mStickyTransformSet) {
+        if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+            // reject this buffer
+            ALOGE("[%s] rejecting buffer: "
+                  "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+                  mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+            return true;
+        }
+    }
+
+    // if the transparent region has changed (this test is
+    // conservative, but that's fine, worst case we're doing
+    // a bit of extra work), we latch the new one and we
+    // trigger a visible-region recompute.
+    //
+    // We latch the transparent region here, instead of above where we latch
+    // the rest of the geometry because it is only content but not necessarily
+    // resize dependent.
+    if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
+        mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+
+        // We also need to update the current state so that
+        // we don't end-up overwriting the drawing state with
+        // this stale current state during the next transaction
+        //
+        // NOTE: We don't need to hold the transaction lock here
+        // because State::active is only accessed from this thread.
+        mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+
+        // recompute visible region
+        mRecomputeVisibleRegions = true;
+    }
+
+    return false;
+}
+
+}  // namespace android
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
new file mode 100644
index 0000000..828cd8b
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_REJECTER_H
+#define ANDROID_LAYER_REJECTER_H
+
+#include "Layer.h"
+#include "SurfaceFlingerConsumer.h"
+
+namespace android {
+    class LayerRejecter : public SurfaceFlingerConsumer::BufferRejecter {
+    public:
+        LayerRejecter(Layer::State &front,
+                      Layer::State &current,
+                      bool &recomputeVisibleRegions,
+                      bool stickySet,
+                      const char *name,
+                      int32_t overrideScalingMode,
+                      bool &freezePositionUpdates);
+
+        virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+
+    private:
+        Layer::State &mFront;
+        Layer::State &mCurrent;
+        bool &mRecomputeVisibleRegions;
+        bool mStickyTransformSet;
+        const char *mName;
+        int32_t mOverrideScalingMode;
+        bool &mFreezeGeometryUpdates;
+    };
+}  // namespace android
+
+#endif  // ANDROID_LAYER_REJECTER_H
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
new file mode 100644
index 0000000..2233e78
--- /dev/null
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerVector.h"
+#include "Layer.h"
+
+namespace android {
+
+LayerVector::LayerVector() = default;
+
+LayerVector::LayerVector(const LayerVector& rhs) : SortedVector<sp<Layer>>(rhs) {
+}
+
+LayerVector::~LayerVector() = default;
+
+int LayerVector::do_compare(const void* lhs, const void* rhs) const
+{
+    // sort layers per layer-stack, then by z-order and finally by sequence
+    const auto& l = *reinterpret_cast<const sp<Layer>*>(lhs);
+    const auto& r = *reinterpret_cast<const sp<Layer>*>(rhs);
+
+    uint32_t ls = l->getCurrentState().layerStack;
+    uint32_t rs = r->getCurrentState().layerStack;
+    if (ls != rs)
+        return ls - rs;
+
+    uint32_t lz = l->getCurrentState().z;
+    uint32_t rz = r->getCurrentState().z;
+    if (lz != rz)
+        return lz - rz;
+
+    return l->sequence - r->sequence;
+}
+
+void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const {
+    for (size_t i = 0; i < size(); i++) {
+        const auto& layer = (*this)[i];
+        auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
+                                                      : layer->getDrawingState();
+        if (state.zOrderRelativeOf != nullptr) {
+            continue;
+        }
+        layer->traverseInZOrder(stateSet, visitor);
+    }
+}
+
+void LayerVector::traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        const auto& layer = (*this)[i];
+        auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
+                                                      : layer->getDrawingState();
+        if (state.zOrderRelativeOf != nullptr) {
+            continue;
+        }
+        layer->traverseInReverseZOrder(stateSet, visitor);
+     }
+}
+} // namespace android
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
new file mode 100644
index 0000000..a9adb41
--- /dev/null
+++ b/services/surfaceflinger/LayerVector.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_LAYER_VECTOR_H
+#define ANDROID_SURFACE_FLINGER_LAYER_VECTOR_H
+
+#include <utils/SortedVector.h>
+#include <utils/RefBase.h>
+
+#include <functional>
+
+namespace android {
+class Layer;
+
+/*
+ * Used by the top-level SurfaceFlinger state and individual layers
+ * to track layers they own sorted according to Z-order. Provides traversal
+ * functions for traversing all owned layers, and their descendents.
+ */
+class LayerVector : public SortedVector<sp<Layer>> {
+public:
+    LayerVector();
+    LayerVector(const LayerVector& rhs);
+    ~LayerVector() override;
+
+    enum class StateSet {
+        Invalid,
+        Current,
+        Drawing,
+    };
+
+    // Sorts layer by layer-stack, Z order, and finally creation order (sequence).
+    int do_compare(const void* lhs, const void* rhs) const override;
+
+    using Visitor = std::function<void(Layer*)>;
+    void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
+    void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
+};
+}
+
+#endif /* ANDROID_SURFACE_FLINGER_LAYER_VECTOR_H_ */
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 974c7a3..bca3430 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -25,7 +25,6 @@
 #include <utils/Log.h>
 
 #include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
 
 #include "MessageQueue.h"
 #include "EventThread.h"
@@ -94,8 +93,8 @@
 {
     mEventThread = eventThread;
     mEvents = eventThread->createEventConnection();
-    mEventTube = mEvents->getDataChannel();
-    mLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,
+    mEvents->stealReceiveChannel(&mEventTube);
+    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT,
             MessageQueue::cb_eventReceiver, this);
 }
 
@@ -150,7 +149,7 @@
 int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
     ssize_t n;
     DisplayEventReceiver::Event buffer[8];
-    while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
+    while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
         for (int i=0 ; i<n ; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 mHandler->dispatchInvalidate();
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 1004f4c..85a33c8 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -25,6 +25,7 @@
 #include <utils/Timers.h>
 #include <utils/Looper.h>
 
+#include <private/gui/BitTube.h>
 #include <gui/DisplayEventReceiver.h>
 
 #include "Barrier.h"
@@ -69,7 +70,7 @@
         MessageQueue& mQueue;
         int32_t mEventMask;
     public:
-        Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
+        explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
         virtual void handleMessage(const Message& message);
         void dispatchRefresh();
         void dispatchInvalidate();
@@ -81,7 +82,7 @@
     sp<Looper> mLooper;
     sp<EventThread> mEventThread;
     sp<IDisplayEventConnection> mEvents;
-    sp<BitTube> mEventTube;
+    gui::BitTube mEventTube;
     sp<Handler> mHandler;
 
 
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index ffaee7a..2ba1b33 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -17,13 +17,16 @@
 #include "MessageQueue.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
+#include "Layer.h"
 
 namespace android {
 
 MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-        const sp<SurfaceFlinger>& flinger) :
+        const sp<SurfaceFlinger>& flinger,
+        const wp<Layer>& layer) :
     mProducer(producer),
-    mFlinger(flinger) {}
+    mFlinger(flinger),
+    mLayer(layer) {}
 
 MonitoredProducer::~MonitoredProducer() {
     // Remove ourselves from SurfaceFlinger's list. We do this asynchronously
@@ -49,7 +52,7 @@
         wp<IBinder> mProducer;
     };
 
-    mFlinger->postMessageAsync(new MessageCleanUpList(mFlinger, asBinder(this)));
+    mFlinger->postMessageAsync(new MessageCleanUpList(mFlinger, asBinder(mProducer)));
 }
 
 status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
@@ -66,8 +69,10 @@
 }
 
 status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
-    return mProducer->dequeueBuffer(slot, fence, w, h, format, usage);
+        uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+        FrameEventHistoryDelta* outTimestamps) {
+    return mProducer->dequeueBuffer(
+            slot, fence, w, h, format, usage, outTimestamps);
 }
 
 status_t MonitoredProducer::detachBuffer(int slot) {
@@ -145,12 +150,20 @@
             outTransformMatrix);
 }
 
+void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+    mProducer->getFrameTimestamps(outDelta);
+}
+
 status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
     return mProducer->getUniqueId(outId);
 }
 
 IBinder* MonitoredProducer::onAsBinder() {
-    return IInterface::asBinder(mProducer).get();
+    return this;
+}
+
+sp<Layer> MonitoredProducer::getLayer() const {
+    return mLayer.promote();
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 66f6cf0..a3ec29d 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -24,13 +24,15 @@
 class IProducerListener;
 class NativeHandle;
 class SurfaceFlinger;
+class Layer;
 
 // MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
 // be notified upon its destruction
-class MonitoredProducer : public IGraphicBufferProducer {
+class MonitoredProducer : public BnGraphicBufferProducer {
 public:
     MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-            const sp<SurfaceFlinger>& flinger);
+            const sp<SurfaceFlinger>& flinger,
+            const wp<Layer>& layer);
     virtual ~MonitoredProducer();
 
     // From IGraphicBufferProducer
@@ -38,7 +40,8 @@
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual status_t setAsyncMode(bool async);
     virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
-            uint32_t h, PixelFormat format, uint32_t usage);
+            uint32_t h, PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps);
     virtual status_t detachBuffer(int slot);
     virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence);
@@ -63,11 +66,17 @@
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
+    virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
     virtual status_t getUniqueId(uint64_t* outId) const override;
 
+    // The Layer which created this producer, and on which queued Buffer's will be displayed.
+    sp<Layer> getLayer() const;
+
 private:
     sp<IGraphicBufferProducer> mProducer;
     sp<SurfaceFlinger> mFlinger;
+    // The Layer which created this producer, and on which queued Buffer's will be displayed.
+    wp<Layer> mLayer;
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
deleted file mode 100644
index 579affb..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-
-#include <cutils/compiler.h>
-
-#include "GLES10RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES10RenderEngine::~GLES10RenderEngine() {
-}
-
-void GLES10RenderEngine::setupLayerBlending(
-#ifdef USE_HWC2
-    bool premultipliedAlpha, bool opaque, float alpha) {
-#else
-    bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
-    // OpenGL ES 1.0 doesn't support texture combiners.
-    // This path doesn't properly handle opaque layers that have non-opaque
-    // alpha values. The alpha channel will be copied into the framebuffer or
-    // screenshot, so if the framebuffer or screenshot is blended on top of
-    // something else,  whatever is below the window will incorrectly show
-    // through.
-#ifdef USE_HWC2
-    if (CC_UNLIKELY(alpha < 1.0f)) {
-        if (premultipliedAlpha) {
-            glColor4f(alpha, alpha, alpha, alpha);
-        } else {
-            glColor4f(1.0f, 1.0f, 1.0f, alpha);
-        }
-#else
-    if (CC_UNLIKELY(alpha < 0xFF)) {
-        GLfloat floatAlpha = alpha * (1.0f / 255.0f);
-        if (premultipliedAlpha) {
-            glColor4f(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
-        } else {
-            glColor4f(1.0f, 1.0f, 1.0f, floatAlpha);
-        }
-#endif
-        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-    } else {
-        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-    }
-
-#ifdef USE_HWC2
-    if (alpha < 1.0f || !opaque) {
-#else
-    if (alpha < 0xFF || !opaque) {
-#endif
-        glEnable(GL_BLEND);
-        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
-                    GL_ONE_MINUS_SRC_ALPHA);
-    } else {
-        glDisable(GL_BLEND);
-    }
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
deleted file mode 100644
index 61abd6a..0000000
--- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef SF_GLES10RENDERENGINE_H_
-#define SF_GLES10RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "GLES11RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GLES10RenderEngine : public GLES11RenderEngine {
-    virtual ~GLES10RenderEngine();
-protected:
-#ifdef USE_HWC2
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
-            float alpha) override;
-#else
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
-#endif
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES10RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
deleted file mode 100644
index 847cdb3..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <ui/Rect.h>
-
-#include <utils/String8.h>
-#include <cutils/compiler.h>
-#include <gui/ISurfaceComposer.h>
-
-#include "GLES11RenderEngine.h"
-#include "Mesh.h"
-#include "Texture.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-GLES11RenderEngine::GLES11RenderEngine() {
-
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glPixelStorei(GL_PACK_ALIGNMENT, 4);
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glShadeModel(GL_FLAT);
-    glDisable(GL_DITHER);
-    glDisable(GL_CULL_FACE);
-
-    const uint16_t protTexData[] = { 0 };
-    glGenTextures(1, &mProtectedTexName);
-    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0,
-            GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-}
-
-GLES11RenderEngine::~GLES11RenderEngine() {
-}
-
-
-size_t GLES11RenderEngine::getMaxTextureSize() const {
-    return mMaxTextureSize;
-}
-
-size_t GLES11RenderEngine::getMaxViewportDims() const {
-    return
-        mMaxViewportDims[0] < mMaxViewportDims[1] ?
-            mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLES11RenderEngine::setViewportAndProjection(
-        size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap,
-        Transform::orientation_flags rotation) {
-    glViewport(0, 0, vpw, vph);
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-
-    size_t l = sourceCrop.left;
-    size_t r = sourceCrop.right;
-
-    // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
-    size_t t = hwh - sourceCrop.top;
-    size_t b = hwh - sourceCrop.bottom;
-
-    if (yswap) {
-        glOrthof(l, r, t, b, 0, 1);
-    } else {
-        glOrthof(l, r, b, t, 0, 1);
-    }
-
-    switch (rotation) {
-        case Transform::ROT_0:
-            break;
-        case Transform::ROT_90:
-            glRotatef(90, 0, 0, 1);
-            break;
-        case Transform::ROT_180:
-            glRotatef(180, 0, 0, 1);
-            break;
-        case Transform::ROT_270:
-            glRotatef(270, 0, 0, 1);
-            break;
-        default:
-            break;
-    }
-
-    glMatrixMode(GL_MODELVIEW);
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupLayerBlending(bool premultipliedAlpha,
-        bool opaque, float alpha) {
-#else
-void GLES11RenderEngine::setupLayerBlending(
-    bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
-    GLenum combineRGB;
-    GLenum combineAlpha;
-    GLenum src0Alpha;
-    GLfloat envColor[4];
-
-#ifdef USE_HWC2
-    if (CC_UNLIKELY(alpha < 1.0f)) {
-#else
-    if (CC_UNLIKELY(alpha < 0xFF)) {
-#endif
-        // Cv = premultiplied ? Cs*alpha : Cs
-        // Av = !opaque       ? As*alpha : As
-        combineRGB   = premultipliedAlpha ? GL_MODULATE : GL_REPLACE;
-        combineAlpha = !opaque            ? GL_MODULATE : GL_REPLACE;
-        src0Alpha    = GL_CONSTANT;
-#ifdef USE_HWC2
-        envColor[0]  = alpha;
-#else
-        envColor[0]  = alpha * (1.0f / 255.0f);
-#endif
-    } else {
-        // Cv = Cs
-        // Av = opaque ? 1.0 : As
-        combineRGB   = GL_REPLACE;
-        combineAlpha = GL_REPLACE;
-        src0Alpha    = opaque ? GL_CONSTANT : GL_TEXTURE;
-        envColor[0]  = 1.0f;
-    }
-
-    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, combineRGB);
-    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
-    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-    if (combineRGB == GL_MODULATE) {
-        glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
-        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
-    }
-    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, combineAlpha);
-    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src0Alpha);
-    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-    if (combineAlpha == GL_MODULATE) {
-        glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
-        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
-    }
-    if (combineRGB == GL_MODULATE || src0Alpha == GL_CONSTANT) {
-        envColor[1] = envColor[0];
-        envColor[2] = envColor[0];
-        envColor[3] = envColor[0];
-        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor);
-    }
-
-#ifdef USE_HWC2
-    if (alpha < 1.0f || !opaque) {
-#else
-    if (alpha < 0xFF || !opaque) {
-#endif
-        glEnable(GL_BLEND);
-        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
-                    GL_ONE_MINUS_SRC_ALPHA);
-    } else {
-        glDisable(GL_BLEND);
-    }
-}
-
-#ifdef USE_HWC2
-void GLES11RenderEngine::setupDimLayerBlending(float alpha) {
-#else
-void GLES11RenderEngine::setupDimLayerBlending(int alpha) {
-#endif
-    glDisable(GL_TEXTURE_EXTERNAL_OES);
-    glDisable(GL_TEXTURE_2D);
-#ifdef USE_HWC2
-    if (alpha == 1.0f) {
-#else
-    if (alpha == 0xFF) {
-#endif
-        glDisable(GL_BLEND);
-    } else {
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-    }
-#ifdef USE_HWC2
-    glColor4f(0, 0, 0, alpha);
-#else
-    glColor4f(0, 0, 0, alpha/255.0f);
-#endif
-}
-
-void GLES11RenderEngine::setupLayerTexturing(const Texture& texture) {
-    GLuint target = texture.getTextureTarget();
-    glBindTexture(target, texture.getTextureName());
-    GLenum filter = GL_NEAREST;
-    if (texture.getFiltering()) {
-        filter = GL_LINEAR;
-    }
-    glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_MAG_FILTER, filter);
-    glTexParameterx(target, GL_TEXTURE_MIN_FILTER, filter);
-    glMatrixMode(GL_TEXTURE);
-    glLoadMatrixf(texture.getMatrix().asArray());
-    glMatrixMode(GL_MODELVIEW);
-    glDisable(GL_TEXTURE_2D);
-    glEnable(GL_TEXTURE_EXTERNAL_OES);
-}
-
-void GLES11RenderEngine::setupLayerBlackedOut() {
-    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
-    glMatrixMode(GL_TEXTURE);
-    glLoadIdentity();
-    glMatrixMode(GL_MODELVIEW);
-    glDisable(GL_TEXTURE_EXTERNAL_OES);
-    glEnable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableTexturing() {
-    glDisable(GL_TEXTURE_EXTERNAL_OES);
-    glDisable(GL_TEXTURE_2D);
-}
-
-void GLES11RenderEngine::disableBlending() {
-    glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::bindImageAsFramebuffer(EGLImageKHR image,
-        uint32_t* texName, uint32_t* fbName, uint32_t* status) {
-    GLuint tname, name;
-    // turn our EGLImage into a texture
-    glGenTextures(1, &tname);
-    glBindTexture(GL_TEXTURE_2D, tname);
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
-
-    // create a Framebuffer Object to render into
-    glGenFramebuffersOES(1, &name);
-    glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
-    glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
-            GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
-
-    *status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
-    *texName = tname;
-    *fbName = name;
-}
-
-void GLES11RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
-    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-    glDeleteFramebuffersOES(1, &fbName);
-    glDeleteTextures(1, &texName);
-}
-
-void GLES11RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
-    glColor4f(r, g, b, a);
-    glDisable(GL_TEXTURE_EXTERNAL_OES);
-    glDisable(GL_TEXTURE_2D);
-    glDisable(GL_BLEND);
-}
-
-void GLES11RenderEngine::drawMesh(const Mesh& mesh) {
-    if (mesh.getTexCoordsSize()) {
-        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-        glTexCoordPointer(mesh.getTexCoordsSize(),
-                GL_FLOAT,
-                mesh.getByteStride(),
-                mesh.getTexCoords());
-    }
-
-    glVertexPointer(mesh.getVertexSize(),
-            GL_FLOAT,
-            mesh.getByteStride(),
-            mesh.getPositions());
-
-    glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
-    if (mesh.getTexCoordsSize()) {
-        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-    }
-}
-
-void GLES11RenderEngine::dump(String8& result) {
-    RenderEngine::dump(result);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
deleted file mode 100644
index 4cd968d..0000000
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef SF_GLES11RENDERENGINE_H_
-#define SF_GLES11RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <GLES/gl.h>
-#include <Transform.h>
-
-#include "RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Mesh;
-class Texture;
-
-class GLES11RenderEngine : public RenderEngine {
-    GLuint mProtectedTexName;
-    GLint mMaxViewportDims[2];
-    GLint mMaxTextureSize;
-
-    virtual void bindImageAsFramebuffer(EGLImageKHR image,
-            uint32_t* texName, uint32_t* fbName, uint32_t* status);
-    virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
-
-public:
-    GLES11RenderEngine();
-
-protected:
-    virtual ~GLES11RenderEngine();
-
-    virtual void dump(String8& result);
-    virtual void setViewportAndProjection(size_t vpw, size_t vph,
-            Rect sourceCrop, size_t hwh, bool yswap,
-            Transform::orientation_flags rotation);
-#ifdef USE_HWC2
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
-            float alpha) override;
-    virtual void setupDimLayerBlending(float alpha) override;
-#else
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
-            int alpha);
-    virtual void setupDimLayerBlending(int alpha);
-#endif
-    virtual void setupLayerTexturing(const Texture& texture);
-    virtual void setupLayerBlackedOut();
-    virtual void setupFillWithColor(float r, float g, float b, float a) ;
-    virtual void disableTexturing();
-    virtual void disableBlending();
-
-    virtual void drawMesh(const Mesh& mesh);
-
-    virtual size_t getMaxTextureSize() const;
-    virtual size_t getMaxViewportDims() const;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES11RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 406e611..04fe182 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include <ui/ColorSpace.h>
+#include <ui/DebugUtils.h>
 #include <ui/Rect.h>
 
 #include <utils/String8.h>
@@ -35,6 +40,73 @@
 #include "Mesh.h"
 #include "Texture.h"
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
+#include <fstream>
+
+// ---------------------------------------------------------------------------
+#ifdef USE_HWC2
+bool checkGlError(const char* op, int lineNumber) {
+    bool errorFound = false;
+    GLint error = glGetError();
+    while (error != GL_NO_ERROR) {
+        errorFound = true;
+        error = glGetError();
+        ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+    }
+    return errorFound;
+}
+
+static constexpr bool outputDebugPPMs = false;
+
+void writePPM(const char* basename, GLuint width, GLuint height) {
+    ALOGV("writePPM #%s: %d x %d", basename, width, height);
+
+    std::vector<GLubyte> pixels(width * height * 4);
+    std::vector<GLubyte> outBuffer(width * height * 3);
+
+    // TODO(courtneygo): We can now have float formats, need
+    // to remove this code or update to support.
+    // Make returned pixels fit in uint32_t, one byte per component
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+    if (checkGlError(__FUNCTION__, __LINE__)) {
+        return;
+    }
+
+    std::string filename(basename);
+    filename.append(".ppm");
+    std::ofstream file(filename.c_str(), std::ios::binary);
+    if (!file.is_open()) {
+        ALOGE("Unable to open file: %s", filename.c_str());
+        ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+              "surfaceflinger to write debug images");
+        return;
+    }
+
+    file << "P6\n";
+    file << width << "\n";
+    file << height << "\n";
+    file << 255 << "\n";
+
+    auto ptr = reinterpret_cast<char*>(pixels.data());
+    auto outPtr = reinterpret_cast<char*>(outBuffer.data());
+    for (int y = height - 1; y >= 0; y--) {
+        char* data = ptr + y * width * sizeof(uint32_t);
+
+        for (GLuint x = 0; x < width; x++) {
+            // Only copy R, G and B components
+            outPtr[0] = data[0];
+            outPtr[1] = data[1];
+            outPtr[2] = data[2];
+            data += sizeof(uint32_t);
+            outPtr += 3;
+        }
+    }
+    file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+}
+#endif
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -59,6 +131,28 @@
             GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
 
     //mColorBlindnessCorrection = M;
+
+#ifdef USE_HWC2
+    // retrieve wide-color and hdr settings from configstore
+    using namespace android::hardware::configstore;
+    using namespace android::hardware::configstore::V1_0;
+
+    mPlatformHasWideColor =
+            getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+    if (mPlatformHasWideColor) {
+        // Compute sRGB to DisplayP3 color transform
+        // NOTE: For now, we are limiting wide-color support to
+        // Display-P3 only.
+        mat3 srgbToP3 = ColorSpace::DisplayP3().getXYZtoRGB() * ColorSpace::sRGB().getRGBtoXYZ();
+
+        // color transform needs to be transposed and expanded to 4x4
+        // to be what the shader wants
+        // mat has an initializer that expands mat3 to mat4, but
+        // not an assignment operator
+        mat4 gamutTransform(transpose(srgbToP3));
+        mSrgbToDisplayP3 = gamutTransform;
+    }
+#endif
 }
 
 GLES20RenderEngine::~GLES20RenderEngine() {
@@ -170,6 +264,42 @@
     }
 }
 
+#ifdef USE_HWC2
+void GLES20RenderEngine::setColorMode(android_color_mode mode) {
+    ALOGV("setColorMode: %s (0x%x)", decodeColorMode(mode).c_str(), mode);
+
+    if (mColorMode == mode) return;
+
+    if (!mPlatformHasWideColor || !mDisplayHasWideColor || mode == HAL_COLOR_MODE_SRGB ||
+        mode == HAL_COLOR_MODE_NATIVE) {
+        // We are returning back to our default color_mode
+        mUseWideColor = false;
+        mWideColorFrameCount = 0;
+    } else {
+        mUseWideColor = true;
+    }
+
+    mColorMode = mode;
+}
+
+void GLES20RenderEngine::setSourceDataSpace(android_dataspace source) {
+    if (source == HAL_DATASPACE_UNKNOWN) {
+        // Treat UNKNOWN as SRGB
+        source = HAL_DATASPACE_V0_SRGB;
+    }
+    mDataSpace = source;
+}
+
+void GLES20RenderEngine::setWideColor(bool hasWideColor) {
+    ALOGV("setWideColor: %s", hasWideColor ? "true" : "false");
+    mDisplayHasWideColor = hasWideColor;
+}
+
+bool GLES20RenderEngine::usesWideColor() {
+    return mUseWideColor;
+}
+#endif
+
 void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
     GLuint target = texture.getTextureTarget();
     glBindTexture(target, texture.getTextureName());
@@ -242,8 +372,6 @@
 
 void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
 
-    ProgramCache::getInstance().useProgram(mState);
-
     if (mesh.getTexCoordsSize()) {
         glEnableVertexAttribArray(Program::texCoords);
         glVertexAttribPointer(Program::texCoords,
@@ -259,7 +387,32 @@
             mesh.getByteStride(),
             mesh.getPositions());
 
+#ifdef USE_HWC2
+    if (usesWideColor()) {
+        Description wideColorState = mState;
+        if (mDataSpace != HAL_DATASPACE_DISPLAY_P3) {
+            wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
+            ALOGV("drawMesh: gamut transform applied");
+        }
+        ProgramCache::getInstance().useProgram(wideColorState);
+
+        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+
+        if (outputDebugPPMs) {
+            std::ostringstream out;
+            out << "/data/texture_out" << mWideColorFrameCount++;
+            writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+        }
+    } else {
+        ProgramCache::getInstance().useProgram(mState);
+
+        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+    }
+#else
+    ProgramCache::getInstance().useProgram(mState);
+
     glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+#endif
 
     if (mesh.getTexCoordsSize()) {
         glDisableVertexAttribArray(Program::texCoords);
@@ -268,6 +421,13 @@
 
 void GLES20RenderEngine::dump(String8& result) {
     RenderEngine::dump(result);
+#ifdef USE_HWC2
+    if (usesWideColor()) {
+        result.append("Wide-color: On\n");
+    } else {
+        result.append("Wide-color: Off\n");
+    }
+#endif
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index 7c3f9b5..19cbb60 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -72,6 +72,27 @@
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
             float alpha) override;
     virtual void setupDimLayerBlending(float alpha) override;
+
+    // Color management related functions and state
+    void setColorMode(android_color_mode mode);
+    void setSourceDataSpace(android_dataspace source);
+    void setWideColor(bool hasWideColor);
+    bool usesWideColor();
+
+    // Current color mode of display using the render engine
+    android_color_mode mColorMode = HAL_COLOR_MODE_NATIVE;
+
+    // Current dataspace of layer being rendered
+    android_dataspace mDataSpace = HAL_DATASPACE_V0_SRGB;
+
+    // Indicate if wide-color mode is needed or not
+    bool mPlatformHasWideColor = false;
+    bool mDisplayHasWideColor = false;
+    bool mUseWideColor = false;
+    uint64_t mWideColorFrameCount = 0;
+
+    // Currently only supporting sRGB and DisplayP3 color spaces
+    mat4 mSrgbToDisplayP3;
 #else
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
             int alpha);
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index 0424e0c..48a8da5 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -17,11 +17,11 @@
 #include <stdint.h>
 
 #include <log/log.h>
+#include <utils/String8.h>
 
 #include "Program.h"
 #include "ProgramCache.h"
 #include "Description.h"
-#include <utils/String8.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index d6a032f..7564269 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
 #include "RenderEngine.h"
-#include "GLES10RenderEngine.h"
-#include "GLES11RenderEngine.h"
 #include "GLES20RenderEngine.h"
 #include "GLExtensions.h"
 #include "Mesh.h"
 
+#include <vector>
+#include <SurfaceFlinger.h>
+
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 // ---------------------------------------------------------------------------
@@ -78,18 +79,21 @@
         LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
     }
 
-    // Also create our EGLContext
-    EGLint contextAttributes[] = {
-            EGL_CONTEXT_CLIENT_VERSION, contextClientVersion,      // MUST be first
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(6);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(contextClientVersion);
 #ifdef EGL_IMG_context_priority
-#ifdef HAS_CONTEXT_PRIORITY
-#warning "using EGL_IMG_context_priority"
-            EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
+    if (SurfaceFlinger::useContextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
 #endif
-#endif
-            EGL_NONE, EGL_NONE
-    };
-    EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes);
+    contextAttributes.push_back(EGL_NONE);
+    contextAttributes.push_back(EGL_NONE);
+
+    EGLContext ctxt = eglCreateContext(display, config, NULL,
+                                       contextAttributes.data());
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -122,10 +126,8 @@
     RenderEngine* engine = NULL;
     switch (version) {
     case GLES_VERSION_1_0:
-        engine = new GLES10RenderEngine();
-        break;
     case GLES_VERSION_1_1:
-        engine = new GLES11RenderEngine();
+        LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
         break;
     case GLES_VERSION_2_0:
     case GLES_VERSION_3_0:
@@ -317,7 +319,7 @@
     KeyedVector<Attribute, EGLint> mList;
     struct Attribute {
         Attribute() : v(0) {};
-        Attribute(EGLint v) : v(v) { }
+        explicit Attribute(EGLint v) : v(v) { }
         EGLint v;
         bool operator < (const Attribute& other) const {
             // this places EGL_NONE at the end
@@ -338,18 +340,18 @@
     public:
         void operator = (EGLint value) {
             if (attribute != EGL_NONE) {
-                v.mList.add(attribute, value);
+                v.mList.add(Attribute(attribute), value);
             }
         }
         operator EGLint () const { return v.mList[attribute]; }
     };
 public:
     EGLAttributeVector() {
-        mList.add(EGL_NONE, EGL_NONE);
+        mList.add(Attribute(EGL_NONE), EGL_NONE);
     }
     void remove(EGLint attribute) {
         if (attribute != EGL_NONE) {
-            mList.removeItem(attribute);
+            mList.removeItem(Attribute(attribute));
         }
     }
     Adder operator [] (EGLint attribute) {
@@ -380,6 +382,7 @@
         attribs[EGL_RED_SIZE]                   = 8;
         attribs[EGL_GREEN_SIZE]                 = 8;
         attribs[EGL_BLUE_SIZE]                  = 8;
+        attribs[EGL_ALPHA_SIZE]                 = 8;
         wantedAttribute                         = EGL_NONE;
         wantedAttributeValue                    = EGL_NONE;
     } else {
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 0259881..8b031bc 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -23,7 +23,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 #include <Transform.h>
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
@@ -98,6 +98,10 @@
 #ifdef USE_HWC2
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, float alpha) = 0;
     virtual void setupDimLayerBlending(float alpha) = 0;
+    virtual void setColorMode(android_color_mode mode) = 0;
+    virtual void setSourceDataSpace(android_dataspace source) = 0;
+    virtual void setWideColor(bool hasWideColor) = 0;
+    virtual bool usesWideColor() = 0;
 #else
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0;
     virtual void setupDimLayerBlending(int alpha) = 0;
diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/services/surfaceflinger/RenderEngine/Texture.h
index 8cf85fc..a07e0c3 100644
--- a/services/surfaceflinger/RenderEngine/Texture.h
+++ b/services/surfaceflinger/RenderEngine/Texture.h
@@ -15,7 +15,7 @@
  */
 
 #include <stdint.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 #ifndef SF_RENDER_ENGINE_TEXTURE_H
 #define SF_RENDER_ENGINE_TEXTURE_H
diff --git a/services/surfaceflinger/StartBootAnimThread.cpp b/services/surfaceflinger/StartBootAnimThread.cpp
new file mode 100644
index 0000000..c3f7296
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/properties.h>
+#include "StartBootAnimThread.h"
+
+namespace android {
+
+StartBootAnimThread::StartBootAnimThread():
+        Thread(false) {
+}
+
+status_t StartBootAnimThread::Start() {
+    return run("SurfaceFlinger::StartBootAnimThread", PRIORITY_NORMAL);
+}
+
+bool StartBootAnimThread::threadLoop() {
+    property_set("service.bootanim.exit", "0");
+    property_set("ctl.start", "bootanim");
+    // Exit immediately
+    return false;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/StartBootAnimThread.h b/services/surfaceflinger/StartBootAnimThread.h
new file mode 100644
index 0000000..dba2bee
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STARTBOOTANIMTHREAD_H
+#define ANDROID_STARTBOOTANIMTHREAD_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+
+namespace android {
+
+class StartBootAnimThread : public Thread {
+// Boot animation is triggered via calls to "property_set()" which can block
+// if init's executing slow operation such as 'mount_all --late' (currently
+// happening 1/10th with fsck)  concurrently. Running in a separate thread
+// allows to pursue the SurfaceFlinger's init process without blocking.
+// see b/34499826.
+public:
+    StartBootAnimThread();
+    status_t Start();
+private:
+    virtual bool threadLoop();
+};
+
+}
+
+#endif // ANDROID_STARTBOOTANIMTHREAD_H
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b009fca..29e7bd6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,29 +21,30 @@
 #include <sys/types.h>
 #include <errno.h>
 #include <math.h>
+#include <mutex>
 #include <dlfcn.h>
 #include <inttypes.h>
 #include <stdatomic.h>
 
 #include <EGL/egl.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
 #include <binder/PermissionCache.h>
 
+#include <dvr/vr_flinger.h>
+
+#include <ui/DebugUtils.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
 
-#include <gui/BitTube.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/Surface.h>
-#include <gui/GraphicBufferAlloc.h>
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
@@ -68,7 +69,9 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "Layer.h"
+#include "LayerVector.h"
 #include "LayerDim.h"
+#include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/FramebufferSurface.h"
@@ -80,6 +83,9 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -92,30 +98,9 @@
 
 namespace android {
 
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
 
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
 
 // ---------------------------------------------------------------------------
 
@@ -125,6 +110,16 @@
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
+bool SurfaceFlinger::useContextPriority;
+int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
+bool SurfaceFlinger::useHwcForRgbToYuv;
+uint64_t SurfaceFlinger::maxVirtualDisplaySize;
+bool SurfaceFlinger::hasSyncFramework;
+bool SurfaceFlinger::useVrFlinger;
+int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+bool SurfaceFlinger::hasWideColorDisplay;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -132,8 +127,12 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
-        mRenderEngine(NULL),
+        mHwc(nullptr),
+        mRealHwc(nullptr),
+        mVrHwc(nullptr),
+        mRenderEngine(nullptr),
         mBootTime(systemTime()),
         mBuiltinDisplays(),
         mVisibleRegionsDirty(false),
@@ -149,6 +148,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(this),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -156,10 +156,43 @@
         mHasPoweredOff(false),
         mFrameBuckets(),
         mTotalTime(0),
-        mLastSwapTime(0)
+        mLastSwapTime(0),
+        mNumLayers(0),
+        mVrFlingerRequestsDisplay(false)
 {
     ALOGI("SurfaceFlinger is starting");
 
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
+    hasSyncFramework = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+
+    useContextPriority = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useContextPriority>(false);
+
+    dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
+
+    useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
+
+    maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+
+    // Vr flinger is only enabled on Daydream ready devices.
+    useVrFlinger = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useVrFlinger>(false);
+
+    maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+
+    hasWideColorDisplay =
+            getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
@@ -184,9 +217,13 @@
     mPropagateBackpressure = !atoi(value);
     ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
 
-    property_get("debug.sf.disable_hwc_vds", value, "0");
-    mUseHwcVirtualDisplays = !atoi(value);
-    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+    property_get("debug.sf.enable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "1");
+    mLayerTripleBufferingDisabled = atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -212,15 +249,29 @@
     startBootAnim();
 }
 
-sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
-{
-    sp<ISurfaceComposerClient> bclient;
-    sp<Client> client(new Client(this));
+static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {
     status_t err = client->initCheck();
     if (err == NO_ERROR) {
-        bclient = client;
+        return client;
     }
-    return bclient;
+    return nullptr;
+}
+
+sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
+    return initClient(new Client(this));
+}
+
+sp<ISurfaceComposerClient> SurfaceFlinger::createScopedConnection(
+        const sp<IGraphicBufferProducer>& gbp) {
+    if (authenticateSurfaceTexture(gbp) == false) {
+        return nullptr;
+    }
+    const auto& layer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+    if (layer == nullptr) {
+        return nullptr;
+    }
+
+   return initClient(new Client(this, layer));
 }
 
 sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
@@ -235,7 +286,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -246,7 +297,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -264,7 +315,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -277,6 +328,7 @@
     // All non-virtual displays are currently considered secure.
     DisplayDeviceState info(type, true);
     mCurrentState.displays.add(mBuiltinDisplays[type], info);
+    mInterceptor.saveDisplayCreation(info);
 }
 
 sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
@@ -287,14 +339,11 @@
     return mBuiltinDisplays[id];
 }
 
-sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc()
-{
-    sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());
-    return gba;
-}
-
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -307,6 +356,10 @@
         window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
     }
 
+    if (mVrFlinger) {
+      mVrFlinger->OnBootFinished();
+    }
+
     // stop boot animation
     // formerly we would just kill the process, but we now ask it to exit so it
     // can choose where to stop the animation.
@@ -449,10 +502,36 @@
     bool mEnabled;
 };
 
+class InjectVSyncSource : public VSyncSource {
+public:
+    InjectVSyncSource() {}
+
+    virtual ~InjectVSyncSource() {}
+
+    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        mCallback = callback;
+    }
+
+    virtual void onInjectSyncEvent(nsecs_t when) {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        mCallback->onVSyncEvent(when);
+    }
+
+    virtual void setVSyncEnabled(bool) {}
+    virtual void setPhaseOffset(nsecs_t) {}
+
+private:
+    std::mutex mCallbackMutex; // Protects the following
+    sp<VSyncSource::Callback> mCallback;
+};
+
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
 
+    ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs);
+
     { // Autolock scope
         Mutex::Autolock _l(mStateLock);
 
@@ -463,18 +542,21 @@
         // start the EventThread
         sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 vsyncPhaseOffsetNs, true, "app");
-        mEventThread = new EventThread(vsyncSrc, *this);
+        mEventThread = new EventThread(vsyncSrc, *this, false);
         sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 sfVsyncPhaseOffsetNs, true, "sf");
-        mSFEventThread = new EventThread(sfVsyncSrc, *this);
+        mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
         mEventQueue.setEventThread(mSFEventThread);
 
-        // set SFEventThread to SCHED_FIFO to minimize jitter
+        // set EventThread and SFEventThread to SCHED_FIFO to minimize jitter
         struct sched_param param = {0};
         param.sched_priority = 2;
         if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, &param) != 0) {
             ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
         }
+        if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, &param) != 0) {
+            ALOGE("Couldn't set SCHED_FIFO for EventThread");
+        }
 
         // Get a RenderEngine for the given display / config (can't fail)
         mRenderEngine = RenderEngine::create(mEGLDisplay,
@@ -484,11 +566,35 @@
     // Drop the state lock while we initialize the hardware composer. We drop
     // the lock because on creation, it will call back into SurfaceFlinger to
     // initialize the primary display.
-    mHwc = new HWComposer(this);
+    LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
+        "Starting with vr flinger active is not currently supported.");
+    mRealHwc = new HWComposer(false);
+    mHwc = mRealHwc;
     mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
 
     Mutex::Autolock _l(mStateLock);
 
+    // Inform native graphics APIs whether the present timestamp is supported:
+    if (getHwComposer().hasCapability(
+            HWC2::Capability::PresentFenceIsNotReliable)) {
+        property_set(kTimestampProperty, "0");
+    } else {
+        property_set(kTimestampProperty, "1");
+    }
+
+    if (useVrFlinger) {
+        auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
+            ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
+            mVrFlingerRequestsDisplay = requestDisplay;
+            signalTransaction();
+        };
+        mVrFlinger = dvr::VrFlinger::Create(mHwc->getComposer(),
+                                            vrFlingerRequestDisplayCallback);
+        if (!mVrFlinger) {
+            ALOGE("Failed to start vrflinger");
+        }
+    }
+
     // retrieve the EGL context that was selected/created
     mEGLContext = mRenderEngine->getEGLContext();
 
@@ -497,7 +603,7 @@
 
     // make the GLContext current so that we can create textures when creating
     // Layers (which may happens before we render something)
-    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
+    getDefaultDisplayDeviceLocked()->makeCurrent(mEGLDisplay, mEGLContext);
 
     mEventControlThread = new EventControlThread(this);
     mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
@@ -510,16 +616,22 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
 
     ALOGV("Done initializing");
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -535,10 +647,34 @@
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const {
     Mutex::Autolock _l(mStateLock);
+    return authenticateSurfaceTextureLocked(bufferProducer);
+}
+
+bool SurfaceFlinger::authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const {
     sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
     return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
 }
 
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+        std::vector<FrameEvent>* outSupported) const {
+    *outSupported = {
+        FrameEvent::REQUESTED_PRESENT,
+        FrameEvent::ACQUIRE,
+        FrameEvent::LATCH,
+        FrameEvent::FIRST_REFRESH_START,
+        FrameEvent::LAST_REFRESH_START,
+        FrameEvent::GPU_COMPOSITION_DONE,
+        FrameEvent::DEQUEUE_READY,
+        FrameEvent::RELEASE,
+    };
+    if (!getHwComposer().hasCapability(
+            HWC2::Capability::PresentFenceIsNotReliable)) {
+        outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
+    }
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
         Vector<DisplayInfo>* configs) {
     if ((configs == NULL) || (display.get() == NULL)) {
@@ -616,7 +752,7 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+        info.appVsyncOffset = vsyncPhaseOffsetNs;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -631,7 +767,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline = hwConfig->getVsyncPeriod() -
-                SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
@@ -656,10 +792,16 @@
 }
 
 int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
-    sp<DisplayDevice> device(getDisplayDevice(display));
+    if (display == NULL) {
+        ALOGE("%s : display is NULL", __func__);
+        return BAD_VALUE;
+    }
+
+    sp<const DisplayDevice> device(getDisplayDevice(display));
     if (device != NULL) {
         return device->getActiveConfig();
     }
+
     return BAD_VALUE;
 }
 
@@ -747,7 +889,7 @@
 }
 
 android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
-    sp<DisplayDevice> device(getDisplayDevice(display));
+    sp<const DisplayDevice> device(getDisplayDevice(display));
     if (device != nullptr) {
         return device->getActiveColorMode();
     }
@@ -756,13 +898,10 @@
 
 void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
         android_color_mode_t mode) {
-    ALOGD("Set active color mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
-          this);
     int32_t type = hw->getDisplayType();
     android_color_mode_t currentMode = hw->getActiveColorMode();
 
     if (mode == currentMode) {
-        ALOGD("Screen type=%d is already in color mode=%d", hw->getDisplayType(), mode);
         return;
     }
 
@@ -771,6 +910,9 @@
         return;
     }
 
+    ALOGD("Set active color mode: %s (%d), type=%d", decodeColorMode(mode).c_str(), mode,
+          hw->getDisplayType());
+
     hw->setActiveColorMode(mode);
     getHwComposer().setActiveColorMode(type, mode);
 }
@@ -791,17 +933,17 @@
             mFlinger.getDisplayColorModes(mDisplay, &modes);
             bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes);
             if (mMode < 0 || !exists) {
-                ALOGE("Attempt to set invalid active color mode = %d for display %p", mMode,
-                        mDisplay.get());
+                ALOGE("Attempt to set invalid active color mode %s (%d) for display %p",
+                      decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
                 return true;
             }
             sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
             if (hw == nullptr) {
-                ALOGE("Attempt to set active color mode = %d for null display %p",
-                        mMode, mDisplay.get());
+                ALOGE("Attempt to set active color mode %s (%d) for null display %p",
+                      decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
             } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
-                ALOGW("Attempt to set active color mode= %d for virtual display",
-                        mMode);
+                ALOGW("Attempt to set active color mode %s %d for virtual display",
+                      decodeColorMode(mMode).c_str(), mMode);
             } else {
                 mFlinger.setActiveColorModeInternal(hw, mMode);
             }
@@ -829,7 +971,7 @@
         HdrCapabilities* outCapabilities) const {
     Mutex::Autolock _l(mStateLock);
 
-    sp<const DisplayDevice> displayDevice(getDisplayDevice(display));
+    sp<const DisplayDevice> displayDevice(getDisplayDeviceLocked(display));
     if (displayDevice == nullptr) {
         ALOGE("getHdrCapabilities: Invalid display %p", displayDevice.get());
         return BAD_VALUE;
@@ -846,10 +988,49 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+    if (enable == mInjectVSyncs) {
+        return NO_ERROR;
+    }
+
+    if (enable) {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections enabled");
+        if (mVSyncInjector.get() == nullptr) {
+            mVSyncInjector = new InjectVSyncSource();
+            mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+        }
+        mEventQueue.setEventThread(mInjectorEventThread);
+    } else {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections disabled");
+        mEventQueue.setEventThread(mSFEventThread);
+        mVSyncInjector.clear();
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs) {
+        ALOGE("VSync Injections not enabled");
+        return BAD_VALUE;
+    }
+    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+        ALOGV("Injecting VSync inside SurfaceFlinger");
+        mVSyncInjector->onInjectSyncEvent(when);
+    }
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
-sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
-    return mEventThread->createEventConnection();
+sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
+        ISurfaceComposer::VsyncSource vsyncSource) {
+    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+        return mSFEventThread->createEventConnection();
+    } else {
+        return mEventThread->createEventConnection();
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -940,12 +1121,24 @@
 
 void SurfaceFlinger::resyncWithRateLimit() {
     static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
-    if (systemTime() - mLastSwapTime > kIgnoreDelay) {
+
+    // No explicit locking is needed here since EventThread holds a lock while calling this method
+    static nsecs_t sLastResyncAttempted = 0;
+    const nsecs_t now = systemTime();
+    if (now - sLastResyncAttempted > kIgnoreDelay) {
         resyncToHardwareVsync(false);
     }
+    sLastResyncAttempted = now;
 }
 
-void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
+void SurfaceFlinger::onVSyncReceived(HWComposer* composer, int32_t type,
+                                     nsecs_t timestamp) {
+    Mutex::Autolock lock(mStateLock);
+    // Ignore any vsyncs from the non-active hardware composer.
+    if (composer != mHwc) {
+        return;
+    }
+
     bool needsHwVsync = false;
 
     { // Scope for the lock
@@ -962,29 +1155,64 @@
     }
 }
 
-void SurfaceFlinger::onHotplugReceived(int32_t disp, bool connected) {
+void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
+    std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+    *compositorTiming = mCompositorTiming;
+}
+
+void SurfaceFlinger::createDefaultDisplayDevice() {
+    const int32_t type = DisplayDevice::DISPLAY_PRIMARY;
+    wp<IBinder> token = mBuiltinDisplays[type];
+
+    // All non-virtual displays are currently considered secure.
+    const bool isSecure = true;
+
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, type, consumer);
+
+    bool hasWideColorModes = false;
+    std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
+    for (android_color_mode_t colorMode : modes) {
+        switch (colorMode) {
+            case HAL_COLOR_MODE_DISPLAY_P3:
+            case HAL_COLOR_MODE_ADOBE_RGB:
+            case HAL_COLOR_MODE_DCI_P3:
+                hasWideColorModes = true;
+                break;
+            default:
+                break;
+        }
+    }
+    sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
+                                             token, fbs, producer, mRenderEngine->getEGLConfig(),
+                                             hasWideColorModes && hasWideColorDisplay);
+    mDisplays.add(token, hw);
+    android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
+    if (hasWideColorModes && hasWideColorDisplay) {
+        defaultColorMode = HAL_COLOR_MODE_SRGB;
+    }
+    setActiveColorModeInternal(hw, defaultColorMode);
+}
+
+void SurfaceFlinger::onHotplugReceived(HWComposer* composer, int32_t disp, bool connected) {
     ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false");
+
+    if (composer->isUsingVrComposer()) {
+        // We handle initializing the primary display device for the VR
+        // window manager hwc explicitly at the time of transition.
+        if (disp != DisplayDevice::DISPLAY_PRIMARY) {
+            ALOGE("External displays are not supported by the vr hardware composer.");
+        }
+        return;
+    }
+
     if (disp == DisplayDevice::DISPLAY_PRIMARY) {
         Mutex::Autolock lock(mStateLock);
-
-        // All non-virtual displays are currently considered secure.
-        bool isSecure = true;
-
-        int32_t type = DisplayDevice::DISPLAY_PRIMARY;
         createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
-        wp<IBinder> token = mBuiltinDisplays[type];
-
-        sp<IGraphicBufferProducer> producer;
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&producer, &consumer,
-                new GraphicBufferAlloc());
-
-        sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc,
-                DisplayDevice::DISPLAY_PRIMARY, consumer);
-        sp<DisplayDevice> hw = new DisplayDevice(this,
-                DisplayDevice::DISPLAY_PRIMARY, disp, isSecure, token, fbs,
-                producer, mRenderEngine->getEGLConfig());
-        mDisplays.add(token, hw);
+        createDefaultDisplayDevice();
     } else {
         auto type = DisplayDevice::DISPLAY_EXTERNAL;
         Mutex::Autolock _l(mStateLock);
@@ -1000,25 +1228,126 @@
     }
 }
 
+void SurfaceFlinger::onInvalidateReceived(HWComposer* composer) {
+    Mutex::Autolock lock(mStateLock);
+    if (composer == mHwc) {
+        repaintEverything();
+    } else {
+        // This isn't from our current hardware composer. If it's a callback
+        // from the real composer, forward the refresh request to vr
+        // flinger. Otherwise ignore it.
+        if (!composer->isUsingVrComposer()) {
+            mVrFlinger->OnHardwareComposerRefresh();
+        }
+    }
+}
+
 void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
     ATRACE_CALL();
     getHwComposer().setVsyncEnabled(disp,
             enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
 }
 
+void SurfaceFlinger::clearHwcLayers(const LayerVector& layers) {
+    for (size_t i = 0; i < layers.size(); ++i) {
+        layers[i]->clearHwcLayers();
+    }
+}
+
+// Note: it is assumed the caller holds |mStateLock| when this is called
+void SurfaceFlinger::resetHwcLocked() {
+    disableHardwareVsync(true);
+    clearHwcLayers(mDrawingState.layersSortedByZ);
+    clearHwcLayers(mCurrentState.layersSortedByZ);
+    // Clear the drawing state so that the logic inside of
+    // handleTransactionLocked will fire. It will determine the delta between
+    // mCurrentState and mDrawingState and re-apply all changes when we make the
+    // transition.
+    mDrawingState.displays.clear();
+    // Release virtual display hwcId during vr mode transition.
+    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
+        const sp<DisplayDevice>& displayDevice = mDisplays[displayId];
+        if (displayDevice->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL) {
+            displayDevice->disconnect(getHwComposer());
+        }
+    }
+    mDisplays.clear();
+}
+
+void SurfaceFlinger::updateVrFlinger() {
+    if (!mVrFlinger)
+        return;
+    bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
+    if (vrFlingerRequestsDisplay == mHwc->isUsingVrComposer()) {
+        return;
+    }
+
+    if (vrFlingerRequestsDisplay && !mVrHwc) {
+        // Construct new HWComposer without holding any locks.
+        mVrHwc = new HWComposer(true);
+
+        // Set up the event handlers. This step is neccessary to initialize the internal state of
+        // the hardware composer object properly. Our callbacks are designed such that if they are
+        // triggered between now and the point where the display is properly re-initialized, they
+        // will not have any effect, so this is safe to do here, before the lock is aquired.
+        mVrHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
+        ALOGV("Vr HWC created");
+    }
+
+    Mutex::Autolock _l(mStateLock);
+
+    if (vrFlingerRequestsDisplay) {
+        resetHwcLocked();
+
+        mHwc = mVrHwc;
+        mVrFlinger->GrantDisplayOwnership();
+
+    } else {
+        mVrFlinger->SeizeDisplayOwnership();
+
+        resetHwcLocked();
+
+        mHwc = mRealHwc;
+        enableHardwareVsync();
+    }
+
+    mVisibleRegionsDirty = true;
+    invalidateHwcGeometry();
+
+    // Explicitly re-initialize the primary display. This is because some other
+    // parts of this class rely on the primary display always being available.
+    createDefaultDisplayDevice();
+
+    // Reset the timing values to account for the period of the swapped in HWC
+    const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
+    const nsecs_t period = activeConfig->getVsyncPeriod();
+    mAnimFrameTracker.setDisplayRefreshPeriod(period);
+    setCompositorTimingSnapped(0, period, 0);
+
+    android_atomic_or(1, &mRepaintEverything);
+    setTransactionFlags(eDisplayTransactionNeeded);
+}
+
 void SurfaceFlinger::onMessageReceived(int32_t what) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
             bool frameMissed = !mHadClientComposition &&
                     mPreviousPresentFence != Fence::NO_FENCE &&
-                    mPreviousPresentFence->getSignalTime() == INT64_MAX;
+                    (mPreviousPresentFence->getSignalTime() ==
+                            Fence::SIGNAL_TIME_PENDING);
             ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
             if (mPropagateBackpressure && frameMissed) {
+                ALOGD("Backpressure trigger, skipping transaction & refresh!");
                 signalLayerUpdate();
                 break;
             }
 
+            // Now that we're going to make it to the handleMessageTransaction()
+            // call below it's safe to call updateVrFlinger(), which will
+            // potentially trigger a display handoff.
+            updateVrFlinger();
+
             bool refreshNeeded = handleMessageTransaction();
             refreshNeeded |= handleMessageInvalidate();
             refreshNeeded |= mRepaintEverything;
@@ -1038,7 +1367,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -1056,14 +1385,14 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
     doComposition();
     postComposition(refreshStartTime);
 
-    mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+    mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
 
     mHadClientComposition = false;
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
@@ -1072,10 +1401,6 @@
                 mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
 
-    // Release any buffers which were replaced this frame
-    for (auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer();
-    }
     mLayersWithQueuedFrames.clear();
 }
 
@@ -1123,40 +1448,133 @@
     }
 }
 
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
 {
     ATRACE_CALL();
     ALOGV("preComposition");
 
     bool needExtraInvalidate = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (layers[i]->onPreComposition()) {
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        if (layer->onPreComposition(refreshStartTime)) {
             needExtraInvalidate = true;
         }
-    }
+    });
+
     if (needExtraInvalidate) {
         signalLayerUpdate();
     }
 }
 
+void SurfaceFlinger::updateCompositorTiming(
+        nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+        std::shared_ptr<FenceTime>& presentFenceTime) {
+    // Update queue of past composite+present times and determine the
+    // most recently known composite to present latency.
+    mCompositePresentTimes.push({compositeTime, presentFenceTime});
+    nsecs_t compositeToPresentLatency = -1;
+    while (!mCompositePresentTimes.empty()) {
+        CompositePresentTime& cpt = mCompositePresentTimes.front();
+        // Cached values should have been updated before calling this method,
+        // which helps avoid duplicate syscalls.
+        nsecs_t displayTime = cpt.display->getCachedSignalTime();
+        if (displayTime == Fence::SIGNAL_TIME_PENDING) {
+            break;
+        }
+        compositeToPresentLatency = displayTime - cpt.composite;
+        mCompositePresentTimes.pop();
+    }
+
+    // Don't let mCompositePresentTimes grow unbounded, just in case.
+    while (mCompositePresentTimes.size() > 16) {
+        mCompositePresentTimes.pop();
+    }
+
+    setCompositorTimingSnapped(
+            vsyncPhase, vsyncInterval, compositeToPresentLatency);
+}
+
+void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase,
+        nsecs_t vsyncInterval, nsecs_t compositeToPresentLatency) {
+    // Integer division and modulo round toward 0 not -inf, so we need to
+    // treat negative and positive offsets differently.
+    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) ?
+            (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
+            ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+
+    // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+    if (idealLatency <= 0) {
+        idealLatency = vsyncInterval;
+    }
+
+    // Snap the latency to a value that removes scheduling jitter from the
+    // composition and present times, which often have >1ms of jitter.
+    // Reducing jitter is important if an app attempts to extrapolate
+    // something (such as user input) to an accurate diasplay time.
+    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // with (presentLatency % interval).
+    nsecs_t bias = vsyncInterval / 2;
+    int64_t extraVsyncs =
+            (compositeToPresentLatency - idealLatency + bias) / vsyncInterval;
+    nsecs_t snappedCompositeToPresentLatency = (extraVsyncs > 0) ?
+            idealLatency + (extraVsyncs * vsyncInterval) : idealLatency;
+
+    std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+    mCompositorTiming.deadline = vsyncPhase - idealLatency;
+    mCompositorTiming.interval = vsyncInterval;
+    mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+}
+
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
 {
     ATRACE_CALL();
     ALOGV("postComposition");
 
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        bool frameLatched = layers[i]->onPostComposition();
-        if (frameLatched) {
-            recordBufferingStats(layers[i]->getName().string(),
-                    layers[i]->getOccupancyHistory(false));
-        }
+    // Release any buffers which were replaced this frame
+    nsecs_t dequeueReadyTime = systemTime();
+    for (auto& layer : mLayersWithQueuedFrames) {
+        layer->releasePendingBuffer(dequeueReadyTime);
     }
 
-    sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+    // |mStateLock| not needed as we are on the main thread
+    const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
+
+    std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
+    if (mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+        glCompositionDoneFenceTime =
+                std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+        mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
+    } else {
+        glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+    }
+    mGlCompositionDoneTimeline.updateSignalTimes();
+
+    sp<Fence> presentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
+    auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+    mDisplayTimeline.push(presentFenceTime);
+    mDisplayTimeline.updateSignalTimes();
+
+    nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
+    nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+
+    // We use the refreshStartTime which might be sampled a little later than
+    // when we started doing work for this frame, but that should be okay
+    // since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(
+        vsyncPhase, vsyncInterval, refreshStartTime, presentFenceTime);
+    CompositorTiming compositorTiming;
+    {
+        std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+        compositorTiming = mCompositorTiming;
+    }
+
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                presentFenceTime, compositorTiming);
+        if (frameLatched) {
+            recordBufferingStats(layer->getName().string(),
+                    layer->getOccupancyHistory(false));
+        }
+    });
 
     if (presentFence->isValid()) {
         if (mPrimaryDispSync.addPresentFence(presentFence)) {
@@ -1166,21 +1584,18 @@
         }
     }
 
-    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
-    if (kIgnorePresentFences) {
+    if (!hasSyncFramework) {
         if (hw->isDisplayOn()) {
             enableHardwareVsync();
         }
     }
 
-    mFenceTracker.addFrame(refreshStartTime, presentFence,
-            hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        if (presentFence->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(presentFence);
+        if (presentFenceTime->isValid()) {
+            mAnimFrameTracker.setActualPresentFence(
+                    std::move(presentFenceTime));
         } else {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
@@ -1199,9 +1614,8 @@
     if (mHasPoweredOff) {
         mHasPoweredOff = false;
     } else {
-        nsecs_t period = mPrimaryDispSync.getPeriod();
         nsecs_t elapsedTime = currentTime - mLastSwapTime;
-        size_t numPeriods = static_cast<size_t>(elapsedTime / period);
+        size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval);
         if (numPeriods < NUM_BUCKETS - 1) {
             mFrameBuckets[numPeriods] += elapsedTime;
         } else {
@@ -1222,7 +1636,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1231,15 +1644,12 @@
             const Transform& tr(displayDevice->getTransform());
             const Rect bounds(displayDevice->getBounds());
             if (displayDevice->isDisplayOn()) {
-                SurfaceFlinger::computeVisibleRegions(layers,
+                computeVisibleRegions(
                         displayDevice->getLayerStack(), dirtyRegion,
                         opaqueRegion);
 
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; i++) {
-                    const sp<Layer>& layer(layers[i]);
-                    const Layer::State& s(layer->getDrawingState());
-                    if (s.layerStack == displayDevice->getLayerStack()) {
+                mDrawingState.traverseInZOrder([&](Layer* layer) {
+                    if (layer->getLayerStack() == displayDevice->getLayerStack()) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -1251,8 +1661,14 @@
                             layer->setHwcLayer(displayDevice->getHwcDisplayId(),
                                     nullptr);
                         }
+                    } else {
+                        // WM changes displayDevice->layerStack upon sleep/awake.
+                        // Here we make sure we delete the HWC layers even if
+                        // WM changed their layer stack.
+                        layer->setHwcLayer(displayDevice->getHwcDisplayId(),
+                                nullptr);
                     }
-                }
+                });
             }
             displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
             displayDevice->undefinedRegion.set(bounds);
@@ -1263,6 +1679,39 @@
     }
 }
 
+// pickColorMode translates a given dataspace into the best available color mode.
+// Currently only support sRGB and Display-P3.
+android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) {
+    switch (dataSpace) {
+        // treat Unknown as regular SRGB buffer, since that's what the rest of the
+        // system expects.
+        case HAL_DATASPACE_UNKNOWN:
+        case HAL_DATASPACE_SRGB:
+        case HAL_DATASPACE_V0_SRGB:
+            return HAL_COLOR_MODE_SRGB;
+            break;
+
+        case HAL_DATASPACE_DISPLAY_P3:
+            return HAL_COLOR_MODE_DISPLAY_P3;
+            break;
+
+        default:
+            // TODO (courtneygo): Do we want to assert an error here?
+            ALOGE("No color mode mapping for %s (%#x)", dataspaceDetails(dataSpace).c_str(),
+                  dataSpace);
+            return HAL_COLOR_MODE_SRGB;
+            break;
+    }
+}
+
+android_dataspace SurfaceFlinger::bestTargetDataSpace(android_dataspace a, android_dataspace b) {
+    // Only support sRGB and Display-P3 right now.
+    if (a == HAL_DATASPACE_DISPLAY_P3 || b == HAL_DATASPACE_DISPLAY_P3) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    }
+    return HAL_DATASPACE_V0_SRGB;
+}
+
 void SurfaceFlinger::setUpHWComposer() {
     ATRACE_CALL();
     ALOGV("setUpHWComposer");
@@ -1305,20 +1754,19 @@
             if (hwcId >= 0) {
                 const Vector<sp<Layer>>& currentLayers(
                         displayDevice->getVisibleLayersSortedByZ());
-                bool foundLayerWithoutHwc = false;
-                for (auto& layer : currentLayers) {
+                for (size_t i = 0; i < currentLayers.size(); i++) {
+                    const auto& layer = currentLayers[i];
                     if (!layer->hasHwcLayer(hwcId)) {
                         auto hwcLayer = mHwc->createLayer(hwcId);
                         if (hwcLayer) {
                             layer->setHwcLayer(hwcId, std::move(hwcLayer));
                         } else {
                             layer->forceClientComposition(hwcId);
-                            foundLayerWithoutHwc = true;
                             continue;
                         }
                     }
 
-                    layer->setGeometry(displayDevice);
+                    layer->setGeometry(displayDevice, i);
                     if (mDebugDisableHWC || mDebugRegion) {
                         layer->forceClientComposition(hwcId);
                     }
@@ -1334,6 +1782,7 @@
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
         auto& displayDevice = mDisplays[displayId];
         const auto hwcId = displayDevice->getHwcDisplayId();
+
         if (hwcId < 0) {
             continue;
         }
@@ -1345,6 +1794,21 @@
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             layer->setPerFrameData(displayDevice);
         }
+
+        if (hasWideColorDisplay) {
+            android_color_mode newColorMode;
+            android_dataspace newDataSpace = HAL_DATASPACE_V0_SRGB;
+
+            for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+                newDataSpace = bestTargetDataSpace(layer->getDataSpace(), newDataSpace);
+                ALOGV("layer: %s, dataspace: %s (%#x), newDataSpace: %s (%#x)",
+                      layer->getName().string(), dataspaceDetails(layer->getDataSpace()).c_str(),
+                      layer->getDataSpace(), dataspaceDetails(newDataSpace).c_str(), newDataSpace);
+            }
+            newColorMode = pickColorMode(newDataSpace);
+
+            setActiveColorModeInternal(displayDevice, newColorMode);
+        }
     }
 
     mPreviousColorMatrix = colorMatrix;
@@ -1398,16 +1862,10 @@
         }
         const auto hwcId = displayDevice->getHwcDisplayId();
         if (hwcId >= 0) {
-            mHwc->commit(hwcId);
+            mHwc->presentAndGetReleaseFences(hwcId);
         }
         displayDevice->onSwapBuffersCompleted();
-        if (displayId == 0) {
-            // Make the default display current because the VirtualDisplayDevice
-            // code cannot deal with dequeueBuffer() being called outside of the
-            // composition loop; however the code below can call glFlush() which
-            // is allowed to (and does in some case) call dequeueBuffer().
-            displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
-        }
+        displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
             if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
@@ -1426,7 +1884,8 @@
     mLastSwapBufferTime = systemTime() - now;
     mDebugInSwapBuffers = 0;
 
-    uint32_t flipCount = getDefaultDisplayDevice()->getPageFlipCount();
+    // |mStateLock| not needed as we are on the main thread
+    uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
     if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
         logFrameStats();
     }
@@ -1463,13 +1922,10 @@
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
-    const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
-    const size_t count = currentLayers.size();
-
     // Notify all layers of available frames
-    for (size_t i = 0; i < count; ++i) {
-        currentLayers[i]->notifyAvailableFrames();
-    }
+    mCurrentState.traverseInZOrder([](Layer* layer) {
+        layer->notifyAvailableFrames();
+    });
 
     /*
      * Traversal of the children
@@ -1477,15 +1933,14 @@
      */
 
     if (transactionFlags & eTraversalNeeded) {
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(currentLayers[i]);
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) continue;
+            if (!trFlags) return;
 
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
                 mVisibleRegionsDirty = true;
-        }
+        });
     }
 
     /*
@@ -1515,9 +1970,9 @@
                         // Call makeCurrent() on the primary display so we can
                         // be sure that nothing associated with this display
                         // is current.
-                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice());
+                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
                         defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext);
-                        sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i)));
+                        sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
                         if (hw != NULL)
                             hw->disconnect(getHwComposer());
                         if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
@@ -1537,7 +1992,7 @@
                         // recreating the DisplayDevice, so we just remove it
                         // from the drawing state, so that it get re-added
                         // below.
-                        sp<DisplayDevice> hw(getDisplayDevice(display));
+                        sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
                         if (hw != NULL)
                             hw->disconnect(getHwComposer());
                         mDisplays.removeItem(display);
@@ -1547,7 +2002,7 @@
                         continue;
                     }
 
-                    const sp<DisplayDevice> disp(getDisplayDevice(display));
+                    const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
                     if (disp != NULL) {
                         if (state.layerStack != draw[i].layerStack) {
                             disp->setLayerStack(state.layerStack);
@@ -1576,8 +2031,7 @@
                     sp<IGraphicBufferProducer> producer;
                     sp<IGraphicBufferProducer> bqProducer;
                     sp<IGraphicBufferConsumer> bqConsumer;
-                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer,
-                            new GraphicBufferAlloc());
+                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
 
                     int32_t hwcId = -1;
                     if (state.isVirtualDisplay()) {
@@ -1586,7 +2040,8 @@
                         // etc.) but no internal state (i.e. a DisplayDevice).
                         if (state.surface != NULL) {
 
-                            if (mUseHwcVirtualDisplays) {
+                            // Allow VR composer to use virtual displays.
+                            if (mUseHwcVirtualDisplays || mHwc == mVrHwc) {
                                 int width = 0;
                                 int status = state.surface->query(
                                         NATIVE_WINDOW_WIDTH, &width);
@@ -1624,24 +2079,19 @@
                                 "adding a supported display, but rendering "
                                 "surface is provided (%p), ignoring it",
                                 state.surface.get());
-                        if (state.type == DisplayDevice::DISPLAY_EXTERNAL) {
-                            hwcId = DisplayDevice::DISPLAY_EXTERNAL;
-                            dispSurface = new FramebufferSurface(*mHwc,
-                                    DisplayDevice::DISPLAY_EXTERNAL,
-                                    bqConsumer);
-                            producer = bqProducer;
-                        } else {
-                            ALOGE("Attempted to add non-external non-virtual"
-                                    " display");
-                        }
+
+                        hwcId = state.type;
+                        dispSurface = new FramebufferSurface(*mHwc, hwcId, bqConsumer);
+                        producer = bqProducer;
                     }
 
                     const wp<IBinder>& display(curr.keyAt(i));
                     if (dispSurface != NULL) {
-                        sp<DisplayDevice> hw = new DisplayDevice(this,
-                                state.type, hwcId, state.isSecure, display,
-                                dispSurface, producer,
-                                mRenderEngine->getEGLConfig());
+                        sp<DisplayDevice> hw =
+                                new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
+                                                  dispSurface, producer,
+                                                  mRenderEngine->getEGLConfig(),
+                                                  hasWideColorDisplay);
                         hw->setLayerStack(state.layerStack);
                         hw->setProjection(state.orientation,
                                 state.viewport, state.frame);
@@ -1678,13 +2128,13 @@
         //
         sp<const DisplayDevice> disp;
         uint32_t currentlayerStack = 0;
-        for (size_t i=0; i<count; i++) {
+        bool first = true;
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
-            const sp<Layer>& layer(currentLayers[i]);
-            uint32_t layerStack = layer->getDrawingState().layerStack;
-            if (i==0 || currentlayerStack != layerStack) {
+            uint32_t layerStack = layer->getLayerStack();
+            if (first || currentlayerStack != layerStack) {
                 currentlayerStack = layerStack;
                 // figure out if this layerstack is mirrored
                 // (more than one display) if so, pick the default display,
@@ -1709,10 +2159,12 @@
                 // could be null when this layer is using a layerStack
                 // that is not visible on any display. Also can occur at
                 // screen off/on times.
-                disp = getDefaultDisplayDevice();
+                disp = getDefaultDisplayDeviceLocked();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1720,9 +2172,9 @@
      * Perform our own transaction if needed
      */
 
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    if (currentLayers.size() > layers.size()) {
-        // layers have been added
+    if (mLayersAdded) {
+        mLayersAdded = false;
+        // Layers have been added.
         mVisibleRegionsDirty = true;
     }
 
@@ -1731,20 +2183,17 @@
     if (mLayersRemoved) {
         mLayersRemoved = false;
         mVisibleRegionsDirty = true;
-        const size_t count = layers.size();
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(layers[i]);
-            if (currentLayers.indexOf(layer) < 0) {
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            if (mLayersPendingRemoval.indexOf(layer) >= 0) {
                 // this layer is not visible anymore
                 // TODO: we could traverse the tree from front to back and
                 //       compute the actual visible region
                 // TODO: we could cache the transformed region
-                const Layer::State& s(layer->getDrawingState());
-                Region visibleReg = s.active.transform.transform(
-                        Region(Rect(s.active.w, s.active.h)));
-                invalidateLayerStack(s.layerStack, visibleReg);
+                Region visibleReg;
+                visibleReg.set(layer->computeScreenBounds());
+                invalidateLayerStack(layer->getLayerStack(), visibleReg);
             }
-        }
+        });
     }
 
     commitTransaction();
@@ -1770,10 +2219,10 @@
 {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
-        for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
-            recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
-                    mLayersPendingRemoval[i]->getOccupancyHistory(true));
-            mLayersPendingRemoval[i]->onRemoved();
+        for (const auto& l : mLayersPendingRemoval) {
+            recordBufferingStats(l->getName().string(),
+                    l->getOccupancyHistory(true));
+            l->onRemoved();
         }
         mLayersPendingRemoval.clear();
     }
@@ -1783,13 +2232,15 @@
     mAnimCompositionPending = mAnimTransactionPending;
 
     mDrawingState = mCurrentState;
+    mDrawingState.traverseInZOrder([](Layer* layer) {
+        layer->commitChildList();
+    });
     mTransactionPending = false;
     mAnimTransactionPending = false;
     mTransactionCV.broadcast();
 }
 
-void SurfaceFlinger::computeVisibleRegions(
-        const LayerVector& currentLayers, uint32_t layerStack,
+void SurfaceFlinger::computeVisibleRegions(uint32_t layerStack,
         Region& outDirtyRegion, Region& outOpaqueRegion)
 {
     ATRACE_CALL();
@@ -1801,16 +2252,13 @@
 
     outDirtyRegion.clear();
 
-    size_t i = currentLayers.size();
-    while (i--) {
-        const sp<Layer>& layer = currentLayers[i];
-
+    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         // start with the whole surface at its current location
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (s.layerStack != layerStack)
-            continue;
+        if (layer->getLayerStack() != layerStack)
+            return;
 
         /*
          * opaqueRegion: area of a surface that is fully opaque.
@@ -1845,12 +2293,12 @@
         // handle hidden surfaces by setting the visible region to empty
         if (CC_LIKELY(layer->isVisible())) {
             const bool translucent = !layer->isOpaque(s);
-            Rect bounds(s.active.transform.transform(layer->computeBounds()));
+            Rect bounds(layer->computeScreenBounds());
             visibleRegion.set(bounds);
+            Transform tr = layer->getTransform();
             if (!visibleRegion.isEmpty()) {
                 // Remove the transparent area from the visible region
                 if (translucent) {
-                    const Transform tr(s.active.transform);
                     if (tr.preserveRects()) {
                         // transform the transparent region
                         transparentRegion = tr.transform(s.activeTransparentRegion);
@@ -1862,7 +2310,7 @@
                 }
 
                 // compute the opaque region
-                const int32_t layerOrientation = s.active.transform.getOrientation();
+                const int32_t layerOrientation = tr.getOrientation();
                 if (s.alpha == 1.0f && !translucent &&
                         ((layerOrientation & Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
@@ -1919,7 +2367,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1938,11 +2386,11 @@
 {
     ALOGV("handlePageFlip");
 
-    Region dirtyRegion;
+    nsecs_t latchTime = systemTime();
 
     bool visibleRegions = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
     bool frameQueued = false;
+    bool newDataLatched = false;
 
     // Store the set of layers that need updates. This set must not change as
     // buffers are being latched, as this could result in a deadlock.
@@ -1953,24 +2401,26 @@
     // 3.) Layer 1 is latched.
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
-    for (size_t i = 0, count = layers.size(); i<count ; i++) {
-        const sp<Layer>& layer(layers[i]);
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (layer->hasQueuedFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(mPrimaryDispSync)) {
-                mLayersWithQueuedFrames.push_back(layer.get());
+                mLayersWithQueuedFrames.push_back(layer);
             } else {
                 layer->useEmptyDamage();
             }
         } else {
             layer->useEmptyDamage();
         }
-    }
+    });
+
     for (auto& layer : mLayersWithQueuedFrames) {
-        const Region dirty(layer->latchBuffer(visibleRegions));
+        const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
         layer->useSurfaceDamage();
-        const Layer::State& s(layer->getDrawingState());
-        invalidateLayerStack(s.layerStack, dirty);
+        invalidateLayerStack(layer->getLayerStack(), dirty);
+        if (!dirty.isEmpty()) {
+            newDataLatched = true;
+        }
     }
 
     mVisibleRegionsDirty |= visibleRegions;
@@ -1983,7 +2433,7 @@
     }
 
     // Only continue with the refresh if there is actually new work to do
-    return !mLayersWithQueuedFrames.empty();
+    return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
 void SurfaceFlinger::invalidateHwcGeometry()
@@ -1992,14 +2442,15 @@
 }
 
 
-void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw,
+void SurfaceFlinger::doDisplayComposition(
+        const sp<const DisplayDevice>& displayDevice,
         const Region& inDirtyRegion)
 {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    bool isHwcDisplay = hw->getHwcDisplayId() >= 0;
+    bool isHwcDisplay = displayDevice->getHwcDisplayId() >= 0;
     if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
         ALOGV("Skipping display composition");
         return;
@@ -2010,35 +2461,35 @@
     Region dirtyRegion(inDirtyRegion);
 
     // compute the invalid region
-    hw->swapRegion.orSelf(dirtyRegion);
+    displayDevice->swapRegion.orSelf(dirtyRegion);
 
-    uint32_t flags = hw->getFlags();
+    uint32_t flags = displayDevice->getFlags();
     if (flags & DisplayDevice::SWAP_RECTANGLE) {
         // we can redraw only what's dirty, but since SWAP_RECTANGLE only
         // takes a rectangle, we must make sure to update that whole
         // rectangle in that case
-        dirtyRegion.set(hw->swapRegion.bounds());
+        dirtyRegion.set(displayDevice->swapRegion.bounds());
     } else {
         if (flags & DisplayDevice::PARTIAL_UPDATES) {
             // We need to redraw the rectangle that will be updated
             // (pushed to the framebuffer).
             // This is needed because PARTIAL_UPDATES only takes one
             // rectangle instead of a region (see DisplayDevice::flip())
-            dirtyRegion.set(hw->swapRegion.bounds());
+            dirtyRegion.set(displayDevice->swapRegion.bounds());
         } else {
             // we need to redraw everything (the whole screen)
-            dirtyRegion.set(hw->bounds());
-            hw->swapRegion = dirtyRegion;
+            dirtyRegion.set(displayDevice->bounds());
+            displayDevice->swapRegion = dirtyRegion;
         }
     }
 
-    if (!doComposeSurfaces(hw, dirtyRegion)) return;
+    if (!doComposeSurfaces(displayDevice, dirtyRegion)) return;
 
     // update the swap region and clear the dirty region
-    hw->swapRegion.orSelf(dirtyRegion);
+    displayDevice->swapRegion.orSelf(dirtyRegion);
 
     // swap buffers (presentation)
-    hw->swapBuffers(getHwComposer());
+    displayDevice->swapBuffers(getHwComposer());
 }
 
 bool SurfaceFlinger::doComposeSurfaces(
@@ -2060,11 +2511,17 @@
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
+#ifdef USE_HWC2
+        mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
+        mRenderEngine->setWideColor(displayDevice->getWideColorSupport());
+#endif
         if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) {
             ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
                   displayDevice->getDisplayName().string());
             eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) {
+
+            // |mStateLock| not needed as we are on the main thread
+            if(!getDefaultDisplayDeviceLocked()->makeCurrent(mEGLDisplay, mEGLContext)) {
               ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
             }
             return false;
@@ -2139,6 +2596,7 @@
                 switch (layer->getCompositionType(hwcId)) {
                     case HWC2::Composition::Cursor:
                     case HWC2::Composition::Device:
+                    case HWC2::Composition::Sideband:
                     case HWC2::Composition::SolidColor: {
                         const Layer::State& state(layer->getDrawingState());
                         if (layer->getClearClientTarget(hwcId) && !firstLayer &&
@@ -2146,7 +2604,7 @@
                                 && hasClientComposition) {
                             // never clear the very first layer since we're
                             // guaranteed the FB is already cleared
-                            layer->clearWithOpenGL(displayDevice, clip);
+                            layer->clearWithOpenGL(displayDevice);
                         }
                         break;
                     }
@@ -2182,8 +2640,8 @@
     return true;
 }
 
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const {
-    const int32_t height = hw->getHeight();
+void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const {
+    const int32_t height = displayDevice->getHeight();
     RenderEngine& engine(getRenderEngine());
     engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
 }
@@ -2191,16 +2649,28 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
         const sp<IBinder>& handle,
         const sp<IGraphicBufferProducer>& gbc,
-        const sp<Layer>& lbc)
+        const sp<Layer>& lbc,
+        const sp<Layer>& parent)
 {
     // add this layer to the current state list
     {
         Mutex::Autolock _l(mStateLock);
-        if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
+        if (mNumLayers >= MAX_LAYERS) {
             return NO_MEMORY;
         }
-        mCurrentState.layersSortedByZ.add(lbc);
+        if (parent == nullptr) {
+            mCurrentState.layersSortedByZ.add(lbc);
+        } else {
+            if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) {
+                ALOGE("addClientLayer called with a removed parent");
+                return NAME_NOT_FOUND;
+            }
+            parent->addChild(lbc);
+        }
+
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2209,25 +2679,52 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) {
     Mutex::Autolock _l(mStateLock);
-    sp<Layer> layer = weakLayer.promote();
-    if (layer == nullptr) {
-        // The layer has already been removed, carry on
+
+    const auto& p = layer->getParent();
+    ssize_t index;
+    if (p != nullptr) {
+        if (topLevelOnly) {
+            return NO_ERROR;
+        }
+
+        sp<Layer> ancestor = p;
+        while (ancestor->getParent() != nullptr) {
+            ancestor = ancestor->getParent();
+        }
+        if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
+            ALOGE("removeLayer called with a layer whose parent has been removed");
+            return NAME_NOT_FOUND;
+        }
+
+        index = p->removeChild(layer);
+    } else {
+        index = mCurrentState.layersSortedByZ.remove(layer);
+    }
+
+    // As a matter of normal operation, the LayerCleaner will produce a second
+    // attempt to remove the surface. The Layer will be kept alive in mDrawingState
+    // so we will succeed in promoting it, but it's already been removed
+    // from mCurrentState. As long as we can find it in mDrawingState we have no problem
+    // otherwise something has gone wrong and we are leaking the layer.
+    if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
+    } else if (index < 0) {
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
-    }
-    return status_t(index);
+    mLayersPendingRemoval.add(layer);
+    mLayersRemoved = true;
+    mNumLayers -= 1 + layer->getChildrenCount();
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2303,6 +2800,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2394,12 +2895,24 @@
         }
         if (what & layer_state_t::eLayerChanged) {
             // NOTE: index needs to be calculated before we update the state
-            ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-            if (layer->setLayer(s.z) && idx >= 0) {
-                mCurrentState.layersSortedByZ.removeAt(idx);
-                mCurrentState.layersSortedByZ.add(layer);
-                // we need traversal (state changed)
-                // AND transaction (list changed)
+            const auto& p = layer->getParent();
+            if (p == nullptr) {
+                ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+                if (layer->setLayer(s.z) && idx >= 0) {
+                    mCurrentState.layersSortedByZ.removeAt(idx);
+                    mCurrentState.layersSortedByZ.add(layer);
+                    // we need traversal (state changed)
+                    // AND transaction (list changed)
+                    flags |= eTransactionNeeded|eTraversalNeeded;
+                }
+            } else {
+                if (p->setChildLayer(layer, s.z)) {
+                    flags |= eTransactionNeeded|eTraversalNeeded;
+                }
+            }
+        }
+        if (what & layer_state_t::eRelativeLayerChanged) {
+            if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
                 flags |= eTransactionNeeded|eTraversalNeeded;
             }
         }
@@ -2429,13 +2942,21 @@
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eFinalCropChanged) {
-            if (layer->setFinalCrop(s.finalCrop))
+            if (layer->setFinalCrop(s.finalCrop, !geometryAppliesWithResize))
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eLayerStackChanged) {
-            // NOTE: index needs to be calculated before we update the state
             ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-            if (layer->setLayerStack(s.layerStack) && idx >= 0) {
+            // We only allow setting layer stacks for top level layers,
+            // everything else inherits layer stack from its parent.
+            if (layer->hasParent()) {
+                ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
+                        layer->getName().string());
+            } else if (idx < 0) {
+                ALOGE("Attempt to set layer stack on layer without parent (%s) that "
+                        "that also does not appear in the top level layer list. Something"
+                        " has gone wrong.", layer->getName().string());
+            } else if (layer->setLayerStack(s.layerStack)) {
                 mCurrentState.layersSortedByZ.removeAt(idx);
                 mCurrentState.layersSortedByZ.add(layer);
                 // we need traversal (state changed)
@@ -2444,10 +2965,30 @@
             }
         }
         if (what & layer_state_t::eDeferTransaction) {
-            layer->deferTransactionUntil(s.handle, s.frameNumber);
+            if (s.barrierHandle != nullptr) {
+                layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
+            } else if (s.barrierGbp != nullptr) {
+                const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+                if (authenticateSurfaceTextureLocked(gbp)) {
+                    const auto& otherLayer =
+                        (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+                    layer->deferTransactionUntil(otherLayer, s.frameNumber);
+                } else {
+                    ALOGE("Attempt to defer transaction to to an"
+                            " unrecognized GraphicBufferProducer");
+                }
+            }
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eReparentChildren) {
+            if (layer->reparentChildren(s.reparentHandle)) {
+                flags |= eTransactionNeeded|eTraversalNeeded;
+            }
+        }
+        if (what & layer_state_t::eDetachChildren) {
+            layer->detachChildren();
+        }
         if (what & layer_state_t::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2461,9 +3002,9 @@
         const String8& name,
         const sp<Client>& client,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
+        uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
+        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
 {
-    //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -2474,15 +3015,17 @@
 
     sp<Layer> layer;
 
+    String8 uniqueName = getUniqueLayerName(name);
+
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceNormal:
             result = createNormalLayer(client,
-                    name, w, h, flags, format,
+                    uniqueName, w, h, flags, format,
                     handle, gbp, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceDim:
             result = createDimLayer(client,
-                    name, w, h, flags,
+                    uniqueName, w, h, flags,
                     handle, gbp, &layer);
             break;
         default:
@@ -2494,15 +3037,42 @@
         return result;
     }
 
-    result = addClientLayer(client, *handle, *gbp, layer);
+    layer->setInfo(windowType, ownerUid);
+
+    result = addClientLayer(client, *handle, *gbp, layer, *parent);
     if (result != NO_ERROR) {
         return result;
     }
+    mInterceptor.saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
     return result;
 }
 
+String8 SurfaceFlinger::getUniqueLayerName(const String8& name)
+{
+    bool matchFound = true;
+    uint32_t dupeCounter = 0;
+
+    // Tack on our counter whether there is a hit or not, so everyone gets a tag
+    String8 uniqueName = name + "#" + String8(std::to_string(dupeCounter).c_str());
+
+    // Loop over layers until we're sure there is no matching name
+    while (matchFound) {
+        matchFound = false;
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            if (layer->getName() == uniqueName) {
+                matchFound = true;
+                uniqueName = name + "#" + String8(std::to_string(++dupeCounter).c_str());
+            }
+        });
+    }
+
+    ALOGD_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), uniqueName.c_str());
+
+    return uniqueName;
+}
+
 status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
         const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
         sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
@@ -2541,10 +3111,11 @@
 
 status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
 {
-    // called by the window manager when it wants to remove a Layer
+    // called by a client when it wants to remove a Layer
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
     if (l != NULL) {
+        mInterceptor.saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
                 "error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2556,7 +3127,13 @@
 {
     // called by ~LayerCleaner() when all references to the IBinder (handle)
     // are gone
-    return removeLayer(layer);
+    sp<Layer> l = layer.promote();
+    if (l == nullptr) {
+        // The layer has already been removed, carry on
+        return NO_ERROR;
+    }
+    // If we have a parent, then we can continue to live as long as it does.
+    return removeLayer(l, true);
 }
 
 // ---------------------------------------------------------------------------
@@ -2582,13 +3159,17 @@
     const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
     const nsecs_t period = activeConfig->getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(period);
+
+    // Use phase of 0 since phase is not known.
+    // Use latency of 0, which will snap to the ideal latency.
+    setCompositorTimingSnapped(0, period, 0);
 }
 
 void SurfaceFlinger::initializeDisplays() {
     class MessageScreenInitialized : public MessageBase {
         SurfaceFlinger* flinger;
     public:
-        MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+        explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
         virtual bool handler() {
             flinger->onInitializeDisplays();
             return true;
@@ -2606,7 +3187,6 @@
     int currentMode = hw->getPowerMode();
 
     if (mode == currentMode) {
-        ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
         return;
     }
 
@@ -2616,11 +3196,20 @@
         return;
     }
 
+    if (mInterceptor.isEnabled()) {
+        Mutex::Autolock _l(mStateLock);
+        ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
+        if (idx < 0) {
+            ALOGW("Surface Interceptor SavePowerMode: invalid display token");
+            return;
+        }
+        mInterceptor.savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+    }
+
     if (currentMode == HWC_POWER_MODE_OFF) {
         // Turn on the display
         getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
             mEventThread->onScreenAcquired();
             resyncToHardwareVsync(true);
@@ -2642,8 +3231,7 @@
             ALOGW("Couldn't set SCHED_OTHER on display off");
         }
 
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (type == DisplayDevice::DISPLAY_PRIMARY) {
             disableHardwareVsync(true); // also cancels any in-progress resync
 
             // FIXME: eventthread only knows about the main display right now
@@ -2653,26 +3241,7 @@
         getHwComposer().setPowerMode(type, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
-    } else if (mode == HWC_POWER_MODE_DOZE ||
-               mode == HWC_POWER_MODE_NORMAL) {
-        // Update display while dozing
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
-            resyncToHardwareVsync(true);
-        }
-    } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
-        // Leave display going to doze
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
-            disableHardwareVsync(true); // also cancels any in-progress resync
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
-        }
-        getHwComposer().setPowerMode(type, mode);
     } else {
-        ALOGE("Attempting to set unknown power mode: %d\n", mode);
         getHwComposer().setPowerMode(type, mode);
     }
 }
@@ -2769,9 +3338,15 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
+                dumpAll = false;
+            }
+
+            if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
+                index++;
+                dumpWideColorInfo(result);
                 dumpAll = false;
             }
         }
@@ -2791,12 +3366,9 @@
 void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */,
         size_t& /* index */, String8& result) const
 {
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         result.appendFormat("%s\n", layer->getName().string());
-    }
+    });
 }
 
 void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
@@ -2815,14 +3387,11 @@
     if (name.isEmpty()) {
         mAnimFrameTracker.dumpStats(result);
     } else {
-        const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-        const size_t count = currentLayers.size();
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(currentLayers[i]);
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             if (name == layer->getName()) {
                 layer->dumpFrameStats(result);
             }
-        }
+        });
     }
 }
 
@@ -2835,14 +3404,11 @@
         index++;
     }
 
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         if (name.isEmpty() || (name == layer->getName())) {
             layer->clearFrameStats();
         }
-    }
+    });
 
     mAnimFrameTracker.clearStats();
 }
@@ -2850,31 +3416,28 @@
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
-    const LayerVector& drawingLayers = mDrawingState.layersSortedByZ;
-    const size_t count = drawingLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(drawingLayers[i]);
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
         layer->logFrameStats();
-    }
+    });
 
     mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
 }
 
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
 {
-    static const char* config =
-            " [sf"
-#ifdef HAS_CONTEXT_PRIORITY
-            " HAS_CONTEXT_PRIORITY"
-#endif
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-            " NEVER_DEFAULT_TO_ASYNC_MODE"
-#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
-            " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
-            "]";
-    result.append(config);
+    result.append(" [sf");
+    result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
+
+    if (isLayerTripleBufferingDisabled())
+        result.append(" DISABLE_TRIPLE_BUFFERING");
+
+    result.appendFormat(" PRESENT_TIME_OFFSET=%" PRId64 , dispSyncPresentTimeOffset);
+    result.appendFormat(" FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
+    result.appendFormat(" MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
+    result.appendFormat(" RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
+    result.appendFormat(" NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
+                        maxFrameBufferAcquiredBuffers);
+    result.append("]");
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2912,6 +3475,16 @@
     }
 }
 
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+    result.appendFormat("Layer frame timestamps:\n");
+
+    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+    const size_t count = currentLayers.size();
+    for (size_t i=0 ; i<count ; i++) {
+        currentLayers[i]->dumpFrameEvents(result);
+    }
+}
+
 void SurfaceFlinger::dumpBufferingStats(String8& result) const {
     result.append("Buffering stats:\n");
     result.append("  [Layer name] <Active time> <Two buffer> "
@@ -2946,6 +3519,30 @@
     result.append("\n");
 }
 
+void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
+    result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
+
+    // TODO: print out if wide-color mode is active or not
+
+    for (size_t d = 0; d < mDisplays.size(); d++) {
+        const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
+        int32_t hwcId = displayDevice->getHwcDisplayId();
+        if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+            continue;
+        }
+
+        result.appendFormat("Display %d color modes:\n", hwcId);
+        std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(hwcId);
+        for (auto&& mode : modes) {
+            result.appendFormat("    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+        }
+
+        android_color_mode_t currentMode = displayDevice->getActiveColorMode();
+        result.appendFormat("    Current color mode: %s (%d)\n",
+                            decodeColorMode(currentMode).c_str(), currentMode);
+    }
+    result.append("\n");
+}
 
 void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
         String8& result) const
@@ -2978,6 +3575,9 @@
     appendGuiConfigString(result);
     result.append("\n");
 
+    result.append("\nWide-Color information:\n");
+    dumpWideColorInfo(result);
+
     colorizer.bold(result);
     result.append("Sync configuration: ");
     colorizer.reset(result);
@@ -2990,9 +3590,9 @@
     result.append("DispSync configuration: ");
     colorizer.reset(result);
     result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
-            "present offset %d ns (refresh %" PRId64 " ns)",
+            "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
         vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs,
-        PRESENT_TIME_OFFSET_FROM_VSYNC_NS, activeConfig->getVsyncPeriod());
+        dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
     result.append("\n");
 
     // Dump static screen stats
@@ -3005,15 +3605,12 @@
     /*
      * Dump the visible layer list
      */
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
     colorizer.bold(result);
-    result.appendFormat("Visible layers (count = %zu)\n", count);
+    result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         layer->dump(result, colorizer);
-    }
+    });
 
     /*
      * Dump Display state
@@ -3036,7 +3633,7 @@
     colorizer.reset(result);
 
     HWComposer& hwc(getHwComposer());
-    sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+    sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
 
     colorizer.bold(result);
     result.appendFormat("EGL implementation : %s\n",
@@ -3091,10 +3688,9 @@
 
         result.appendFormat("Display %d HWC layers:\n", hwcId);
         Layer::miniDumpHeader(result);
-        for (size_t l = 0; l < count; l++) {
-            const sp<Layer>& layer(currentLayers[l]);
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             layer->miniDump(result, hwcId);
-        }
+        });
         result.append("\n");
     }
 
@@ -3131,7 +3727,7 @@
         // Just use the primary display so we have something to return
         dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
     }
-    return getDisplayDevice(dpy)->getVisibleLayersSortedByZ();
+    return getDisplayDeviceLocked(dpy)->getVisibleLayersSortedByZ();
 }
 
 bool SurfaceFlinger::startDdmConnection()
@@ -3152,13 +3748,10 @@
     return true;
 }
 
-status_t SurfaceFlinger::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
     switch (code) {
         case CREATE_CONNECTION:
         case CREATE_DISPLAY:
-        case SET_TRANSACTION_STATE:
         case BOOT_FINISHED:
         case CLEAR_ANIMATION_FRAME_STATS:
         case GET_ANIMATION_FRAME_STATS:
@@ -3171,12 +3764,22 @@
             const int uid = ipc->getCallingUid();
             if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
                     !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
-                ALOGE("Permission Denial: "
-                        "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+                ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
             break;
         }
+        /*
+         * Calling setTransactionState is safe, because you need to have been
+         * granted a reference to Client* and Handle* to do anything with it.
+         *
+         * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h
+         */
+        case SET_TRANSACTION_STATE:
+        case CREATE_SCOPED_CONNECTION:
+        {
+            return OK;
+        }
         case CAPTURE_SCREEN:
         {
             // codes that require permission check
@@ -3185,13 +3788,22 @@
             const int uid = ipc->getCallingUid();
             if ((uid != AID_GRAPHICS) &&
                     !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
-                ALOGE("Permission Denial: "
-                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
+                ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
             break;
         }
     }
+    return OK;
+}
+
+status_t SurfaceFlinger::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    status_t credentialCheck = CheckTransactCodeCredentials(code);
+    if (credentialCheck != OK) {
+        return credentialCheck;
+    }
 
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
@@ -3250,7 +3862,6 @@
                 reply->writeInt32(mDebugDisableHWC);
                 return NO_ERROR;
             case 1013: {
-                Mutex::Autolock _l(mStateLock);
                 sp<const DisplayDevice> hw(getDefaultDisplayDevice());
                 reply->writeInt32(hw->getPageFlipCount());
                 return NO_ERROR;
@@ -3322,6 +3933,18 @@
                 mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
                 return NO_ERROR;
             }
+            case 1020: { // Layer updates interceptor
+                n = data.readInt32();
+                if (n) {
+                    ALOGV("Interceptor enabled");
+                    mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+                }
+                else{
+                    ALOGV("Interceptor disabled");
+                    mInterceptor.disable();
+                }
+                return NO_ERROR;
+            }
             case 1021: { // Disable HWC virtual displays
                 n = data.readInt32();
                 mUseHwcVirtualDisplays = !n;
@@ -3423,7 +4046,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3457,7 +4080,7 @@
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
 
     if (CC_UNLIKELY(display == 0))
@@ -3508,7 +4131,7 @@
                 const sp<IBinder>& display,
                 const sp<IGraphicBufferProducer>& producer,
                 Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                uint32_t minLayerZ, uint32_t maxLayerZ,
+                int32_t minLayerZ, int32_t maxLayerZ,
                 bool useIdentityTransform,
                 Transform::orientation_flags rotation,
                 bool isLocalScreenshot)
@@ -3525,7 +4148,7 @@
         }
         virtual bool handler() {
             Mutex::Autolock _l(flinger->mStateLock);
-            sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
+            sp<const DisplayDevice> hw(flinger->getDisplayDeviceLocked(display));
             result = flinger->captureScreenImplLocked(hw, producer,
                     sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
                     useIdentityTransform, rotation, isLocalScreenshot);
@@ -3557,7 +4180,7 @@
 void SurfaceFlinger::renderScreenImplLocked(
         const sp<const DisplayDevice>& hw,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation)
 {
     ATRACE_CALL();
@@ -3601,20 +4224,24 @@
     // redraw the screen entirely...
     engine.clearWithColor(0, 0, 0, 1);
 
-    const LayerVector& layers( mDrawingState.layersSortedByZ );
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; ++i) {
-        const sp<Layer>& layer(layers[i]);
-        const Layer::State& state(layer->getDrawingState());
-        if (state.layerStack == hw->getLayerStack()) {
-            if (state.z >= minLayerZ && state.z <= maxLayerZ) {
-                if (layer->isVisible()) {
-                    if (filtering) layer->setFiltering(true);
-                    layer->draw(hw, useIdentityTransform);
-                    if (filtering) layer->setFiltering(false);
-                }
-            }
+    // We loop through the first level of layers without traversing,
+    // as we need to interpret min/max layer Z in the top level Z space.
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        if (layer->getLayerStack() != hw->getLayerStack()) {
+            continue;
         }
+        const Layer::State& state(layer->getDrawingState());
+        if (state.z < minLayerZ || state.z > maxLayerZ) {
+            continue;
+        }
+        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+            if (!layer->isVisible()) {
+                return;
+            }
+            if (filtering) layer->setFiltering(true);
+            layer->draw(hw, useIdentityTransform);
+            if (filtering) layer->setFiltering(false);
+        });
     }
 
     hw->setViewportAndProjection();
@@ -3625,7 +4252,7 @@
         const sp<const DisplayDevice>& hw,
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform, Transform::orientation_flags rotation,
         bool isLocalScreenshot)
 {
@@ -3649,16 +4276,16 @@
     reqHeight = (!reqHeight) ? hw_h : reqHeight;
 
     bool secureLayerIsVisible = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i = 0 ; i < count ; ++i) {
-        const sp<Layer>& layer(layers[i]);
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
         const Layer::State& state(layer->getDrawingState());
-        if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ &&
-                state.z <= maxLayerZ && layer->isVisible() &&
-                layer->isSecure()) {
-            secureLayerIsVisible = true;
+        if ((layer->getLayerStack() != hw->getLayerStack()) ||
+                (state.z < minLayerZ || state.z > maxLayerZ)) {
+            continue;
         }
+        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer *layer) {
+            secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
+                    layer->isSecure());
+        });
     }
 
     if (!isLocalScreenshot && secureLayerIsVisible) {
@@ -3786,7 +4413,7 @@
 }
 
 void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
-        const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
+        const sp<const DisplayDevice>& hw, int32_t minLayerZ, int32_t maxLayerZ) {
     if (DEBUG_SCREENSHOTS) {
         for (size_t y=0 ; y<h ; y++) {
             uint32_t const * p = (uint32_t const *)vaddr + y*s;
@@ -3797,81 +4424,34 @@
         ALOGE("*** we just took a black screenshot ***\n"
                 "requested minz=%d, maxz=%d, layerStack=%d",
                 minLayerZ, maxLayerZ, hw->getLayerStack());
-        const LayerVector& layers( mDrawingState.layersSortedByZ );
-        const size_t count = layers.size();
-        for (size_t i=0 ; i<count ; ++i) {
-            const sp<Layer>& layer(layers[i]);
+
+        size_t i = 0;
+        for (const auto& layer : mDrawingState.layersSortedByZ) {
             const Layer::State& state(layer->getDrawingState());
-            const bool visible = (state.layerStack == hw->getLayerStack())
-                                && (state.z >= minLayerZ && state.z <= maxLayerZ)
-                                && (layer->isVisible());
-            ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
-                    visible ? '+' : '-',
-                            i, layer->getName().string(), state.layerStack, state.z,
+            if (layer->getLayerStack() == hw->getLayerStack() && state.z >= minLayerZ &&
+                    state.z <= maxLayerZ) {
+                layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+                    ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
+                            layer->isVisible() ? '+' : '-',
+                            i, layer->getName().string(), layer->getLayerStack(), state.z,
                             layer->isVisible(), state.flags, state.alpha);
+                    i++;
+                });
+            }
         }
     }
 }
 
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
-        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
-    return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
 // ---------------------------------------------------------------------------
 
-SurfaceFlinger::LayerVector::LayerVector() {
+void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverseInZOrder(stateSet, visitor);
 }
 
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
-    : SortedVector<sp<Layer> >(rhs) {
+void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
 }
 
-int SurfaceFlinger::LayerVector::do_compare(const void* lhs,
-    const void* rhs) const
-{
-    // sort layers per layer-stack, then by z-order and finally by sequence
-    const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs));
-    const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs));
-
-    uint32_t ls = l->getCurrentState().layerStack;
-    uint32_t rs = r->getCurrentState().layerStack;
-    if (ls != rs)
-        return ls - rs;
-
-    uint32_t lz = l->getCurrentState().z;
-    uint32_t rz = r->getCurrentState().z;
-    if (lz != rz)
-        return lz - rz;
-
-    return l->sequence - r->sequence;
-}
-
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
-    : type(DisplayDevice::DISPLAY_ID_INVALID),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
-    DisplayDevice::DisplayType type, bool isSecure)
-    : type(type),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(isSecure) {
-    viewport.makeInvalid();
-    frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
 }; // namespace android
 
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b98924b..9239538 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SURFACE_FLINGER_H
 #define ANDROID_SURFACE_FLINGER_H
 
+#include <memory>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -35,11 +36,11 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
-#include <binder/IMemory.h>
-
+#include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
+#include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/OccupancyTracker.h>
@@ -53,15 +54,20 @@
 #include "Barrier.h"
 #include "DisplayDevice.h"
 #include "DispSync.h"
-#include "FenceTracker.h"
 #include "FrameTracker.h"
+#include "LayerVector.h"
 #include "MessageQueue.h"
+#include "SurfaceInterceptor.h"
+#include "StartBootAnimThread.h"
 
 #include "DisplayHardware/HWComposer.h"
 #include "Effects/Daltonizer.h"
 
 #include <map>
+#include <mutex>
+#include <queue>
 #include <string>
+#include <utility>
 
 namespace android {
 
@@ -70,12 +76,17 @@
 class Client;
 class DisplayEventConnection;
 class EventThread;
-class IGraphicBufferAlloc;
 class Layer;
 class LayerDim;
 class Surface;
 class RenderEngine;
 class EventControlThread;
+class VSyncSource;
+class InjectVSyncSource;
+
+namespace dvr {
+class VrFlinger;
+} // namespace dvr
 
 // ---------------------------------------------------------------------------
 
@@ -91,6 +102,61 @@
                        private HWComposer::EventHandler
 {
 public:
+
+    // This is the phase offset in nanoseconds of the software vsync event
+    // relative to the vsync event reported by HWComposer.  The software vsync
+    // event is when SurfaceFlinger and Choreographer-based applications run each
+    // frame.
+    //
+    // This phase offset allows adjustment of the minimum latency from application
+    // wake-up time (by Choreographer) to the time at which the resulting window
+    // image is displayed.  This value may be either positive (after the HW vsync)
+    // or negative (before the HW vsync). Setting it to 0 will result in a lower
+    // latency bound of two vsync periods because the app and SurfaceFlinger
+    // will run just after the HW vsync.  Setting it to a positive number will
+    // result in the minimum latency being:
+    //
+    //     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+    //
+    // Note that reducing this latency makes it more likely for the applications
+    // to not have their window content image ready in time.  When this happens
+    // the latency will end up being an additional vsync period, and animations
+    // will hiccup.  Therefore, this latency should be tuned somewhat
+    // conservatively (or at least with awareness of the trade-off being made).
+    static int64_t vsyncPhaseOffsetNs;
+    static int64_t sfVsyncPhaseOffsetNs;
+
+    // If fences from sync Framework are supported.
+    static bool hasSyncFramework;
+
+    // Instruct the Render Engine to use EGL_IMG_context_priority is available.
+    static bool useContextPriority;
+
+    // The offset in nanoseconds to use when DispSync timestamps present fence
+    // signaling time.
+    static int64_t dispSyncPresentTimeOffset;
+
+    // Some hardware can do RGB->YUV conversion more efficiently in hardware
+    // controlled by HWC than in hardware controlled by the video encoder.
+    // This instruct VirtualDisplaySurface to use HWC for such conversion on
+    // GL composition.
+    static bool useHwcForRgbToYuv;
+
+    // Maximum dimension supported by HWC for virtual display.
+    // Equal to min(max_height, max_width).
+    static uint64_t maxVirtualDisplaySize;
+
+    // Controls the number of buffers SurfaceFlinger will allocate for use in
+    // FramebufferSurface
+    static int64_t maxFrameBufferAcquiredBuffers;
+
+    // Indicate if platform supports color management on its
+    // wide-color display. This is typically found on devices
+    // with wide gamut (e.g. Display-P3) display.
+    // This also allows devices with wide-color displays that don't
+    // want to support color management to disable color management.
+    static bool hasWideColorDisplay;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -118,7 +184,8 @@
 
     // returns the default Display
     sp<const DisplayDevice> getDefaultDisplayDevice() const {
-        return getDisplayDevice(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
+        Mutex::Autolock _l(mStateLock);
+        return getDefaultDisplayDeviceLocked();
     }
 
     // utility function to delete a texture on the main thread
@@ -145,9 +212,13 @@
         return *mRenderEngine;
     }
 
+    bool authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const;
+
 private:
     friend class Client;
     friend class DisplayEventConnection;
+    friend class EventThread;
     friend class Layer;
     friend class MonitoredProducer;
 
@@ -156,6 +227,7 @@
     enum { LOG_FRAME_STATS_PERIOD =  30*60*60 };
 
     static const size_t MAX_LAYERS = 4096;
+    static constexpr const char* kTimestampProperty = "service.sf.present_timestamp";
 
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -164,33 +236,23 @@
      * Internal data structures
      */
 
-    class LayerVector : public SortedVector< sp<Layer> > {
+    class State {
     public:
-        LayerVector();
-        LayerVector(const LayerVector& rhs);
-        virtual int do_compare(const void* lhs, const void* rhs) const;
-    };
+        explicit State(LayerVector::StateSet set) : stateSet(set) {}
+        State& operator=(const State& other) {
+            // We explicitly don't copy stateSet so that, e.g., mDrawingState
+            // always uses the Drawing StateSet.
+            layersSortedByZ = other.layersSortedByZ;
+            displays = other.displays;
+            return *this;
+        }
 
-    struct DisplayDeviceState {
-        DisplayDeviceState();
-        DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
-        bool isValid() const { return type >= 0; }
-        bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
-        bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
-        DisplayDevice::DisplayType type;
-        sp<IGraphicBufferProducer> surface;
-        uint32_t layerStack;
-        Rect viewport;
-        Rect frame;
-        uint8_t orientation;
-        uint32_t width, height;
-        String8 displayName;
-        bool isSecure;
-    };
-
-    struct State {
+        const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
         LayerVector layersSortedByZ;
         DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+
+        void traverseInZOrder(const LayerVector::Visitor& visitor) const;
+        void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
 
     /* ------------------------------------------------------------------------
@@ -204,7 +266,7 @@
      * ISurfaceComposer interface
      */
     virtual sp<ISurfaceComposerClient> createConnection();
-    virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
+    virtual sp<ISurfaceComposerClient> createScopedConnection(const sp<IGraphicBufferProducer>& gbp);
     virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
     virtual void destroyDisplay(const sp<IBinder>& display);
     virtual sp<IBinder> getBuiltInDisplay(int32_t id);
@@ -213,11 +275,14 @@
     virtual void bootFinished();
     virtual bool authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection();
+    virtual status_t getSupportedFrameTimestamps(
+            std::vector<FrameEvent>* outSupported) const;
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection(
+            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform, ISurfaceComposer::Rotation rotation);
     virtual status_t getDisplayStats(const sp<IBinder>& display,
             DisplayStatInfo* stats);
@@ -234,6 +299,9 @@
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
     virtual status_t getHdrCapabilities(const sp<IBinder>& display,
             HdrCapabilities* outCapabilities) const;
+    virtual status_t enableVSyncInjections(bool enable);
+    virtual status_t injectVSync(nsecs_t when);
+
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -248,8 +316,9 @@
     /* ------------------------------------------------------------------------
      * HWComposer::EventHandler interface
      */
-    virtual void onVSyncReceived(int type, nsecs_t timestamp);
-    virtual void onHotplugReceived(int disp, bool connected);
+    virtual void onVSyncReceived(HWComposer* composer, int type, nsecs_t timestamp);
+    virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected);
+    virtual void onInvalidateReceived(HWComposer* composer);
 
     /* ------------------------------------------------------------------------
      * Message handling
@@ -292,7 +361,7 @@
      * Transactions
      */
     uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t peekTransactionFlags(uint32_t flags);
+    uint32_t peekTransactionFlags();
     uint32_t setTransactionFlags(uint32_t flags);
     void commitTransaction();
     uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
@@ -303,7 +372,8 @@
      */
     status_t createLayer(const String8& name, const sp<Client>& client,
             uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-            sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp);
+            uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
+            sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
 
     status_t createNormalLayer(const sp<Client>& client, const String8& name,
             uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
@@ -314,6 +384,8 @@
             uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
             sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
 
+    String8 getUniqueLayerName(const String8& name);
+
     // called in response to the window-manager calling
     // ISurfaceComposerClient::destroySurface()
     status_t onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle);
@@ -324,13 +396,14 @@
     status_t onLayerDestroyed(const wp<Layer>& layer);
 
     // remove a layer from SurfaceFlinger immediately
-    status_t removeLayer(const wp<Layer>& layer);
+    status_t removeLayer(const sp<Layer>& layer, bool topLevelOnly = false);
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client,
             const sp<IBinder>& handle,
             const sp<IGraphicBufferProducer>& gbc,
-            const sp<Layer>& lbc);
+            const sp<Layer>& lbc,
+            const sp<Layer>& parent);
 
     /* ------------------------------------------------------------------------
      * Boot animation, on/off animations and screen capture
@@ -341,17 +414,19 @@
     void renderScreenImplLocked(
             const sp<const DisplayDevice>& hw,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation);
 
     status_t captureScreenImplLocked(
             const sp<const DisplayDevice>& hw,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform, Transform::orientation_flags rotation,
             bool isLocalScreenshot);
 
+    sp<StartBootAnimThread> mStartBootAnimThread = nullptr;
+
     /* ------------------------------------------------------------------------
      * EGL
      */
@@ -367,16 +442,33 @@
     // Create an IBinder for a builtin display and add it to current state
     void createBuiltinDisplayLocked(DisplayDevice::DisplayType type);
 
-    // NOTE: can only be called from the main thread or with mStateLock held
+
     sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
+      Mutex::Autolock _l(mStateLock);
+      return getDisplayDeviceLocked(dpy);
+    }
+
+    sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) {
+      Mutex::Autolock _l(mStateLock);
+      return getDisplayDeviceLocked(dpy);
+    }
+
+    // NOTE: can only be called from the main thread or with mStateLock held
+    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) const {
         return mDisplays.valueFor(dpy);
     }
 
     // NOTE: can only be called from the main thread or with mStateLock held
-    sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) {
+    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) {
         return mDisplays.valueFor(dpy);
     }
 
+    sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
+        return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
+    }
+
+    void createDefaultDisplayDevice();
+
     int32_t getDisplayType(const sp<IBinder>& display) {
         if (!display.get()) return NAME_NOT_FOUND;
         for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
@@ -405,24 +497,35 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    static void computeVisibleRegions(
-            const LayerVector& currentLayers, uint32_t layerStack,
+    void computeVisibleRegions(uint32_t layerStack,
             Region& dirtyRegion, Region& opaqueRegion);
 
-    void preComposition();
+    void preComposition(nsecs_t refreshStartTime);
     void postComposition(nsecs_t refreshStartTime);
+    void updateCompositorTiming(
+            nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+            std::shared_ptr<FenceTime>& presentFenceTime);
+    void setCompositorTimingSnapped(
+            nsecs_t vsyncPhase, nsecs_t vsyncInterval,
+            nsecs_t compositeToPresentLatency);
     void rebuildLayerStacks();
+
+    // Given a dataSpace, returns the appropriate color_mode to use
+    // to display that dataSpace.
+    android_color_mode pickColorMode(android_dataspace dataSpace);
+    android_dataspace bestTargetDataSpace(android_dataspace a, android_dataspace b);
+
     void setUpHWComposer();
     void doComposition();
     void doDebugFlashRegions();
-    void doDisplayComposition(const sp<const DisplayDevice>& hw, const Region& dirtyRegion);
+    void doDisplayComposition(const sp<const DisplayDevice>& displayDevice, const Region& dirtyRegion);
 
     // compose surfaces for display hw. this fails if using GL and the surface
     // has been destroyed and is no longer valid.
-    bool doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty);
+    bool doComposeSurfaces(const sp<const DisplayDevice>& displayDevice, const Region& dirty);
 
     void postFramebuffer();
-    void drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const;
+    void drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const;
 
     /* ------------------------------------------------------------------------
      * Display management
@@ -431,11 +534,13 @@
     /* ------------------------------------------------------------------------
      * VSync
      */
-     void enableHardwareVsync();
-     void resyncToHardwareVsync(bool makeAvailable);
-     void disableHardwareVsync(bool makeUnavailable);
+    void enableHardwareVsync();
+    void resyncToHardwareVsync(bool makeAvailable);
+    void disableHardwareVsync(bool makeUnavailable);
+
 public:
-     void resyncWithRateLimit();
+    void resyncWithRateLimit();
+    void getCompositorTiming(CompositorTiming* compositorTiming);
 private:
 
     /* ------------------------------------------------------------------------
@@ -446,21 +551,36 @@
     void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
     void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
     bool startDdmConnection();
-    static void appendSfConfigString(String8& result);
+    void appendSfConfigString(String8& result) const;
     void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
             const sp<const DisplayDevice>& hw,
-            uint32_t minLayerZ, uint32_t maxLayerZ);
+            int32_t minLayerZ, int32_t maxLayerZ);
 
     void logFrameStats();
 
     void dumpStaticScreenStats(String8& result) const;
+    // Not const because each Layer needs to query Fences and cache timestamps.
+    void dumpFrameEventsLocked(String8& result);
 
     void recordBufferingStats(const char* layerName,
             std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(String8& result) const;
+    void dumpWideColorInfo(String8& result) const;
 
-    bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
-            FrameTimestamps* outTimestamps);
+    bool isLayerTripleBufferingDisabled() const {
+        return this->mLayerTripleBufferingDisabled;
+    }
+
+#ifdef USE_HWC2
+    /* ------------------------------------------------------------------------
+     * VrFlinger
+     */
+    void clearHwcLayers(const LayerVector& layers);
+    void resetHwcLocked();
+
+    // Check to see if we should handoff to vr flinger.
+    void updateVrFlinger();
+#endif
 
     /* ------------------------------------------------------------------------
      * Attributes
@@ -468,35 +588,47 @@
 
     // access must be protected by mStateLock
     mutable Mutex mStateLock;
-    State mCurrentState;
+    State mCurrentState{LayerVector::StateSet::Current};
     volatile int32_t mTransactionFlags;
     Condition mTransactionCV;
     bool mTransactionPending;
     bool mAnimTransactionPending;
-    Vector< sp<Layer> > mLayersPendingRemoval;
+    SortedVector< sp<Layer> > mLayersPendingRemoval;
     SortedVector< wp<IBinder> > mGraphicBufferProducerList;
 
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved;
+    bool mLayersAdded;
 
     // access must be protected by mInvalidateLock
     volatile int32_t mRepaintEverything;
 
-    // constant members (no synchronization needed for access)
+    // current, real and vr hardware composers.
     HWComposer* mHwc;
+#ifdef USE_HWC2
+    HWComposer* mRealHwc;
+    HWComposer* mVrHwc;
+#endif
+    // constant members (no synchronization needed for access)
     RenderEngine* mRenderEngine;
     nsecs_t mBootTime;
     bool mGpuToCpuSupported;
     sp<EventThread> mEventThread;
     sp<EventThread> mSFEventThread;
+    sp<EventThread> mInjectorEventThread;
+    sp<InjectVSyncSource> mVSyncInjector;
     sp<EventControlThread> mEventControlThread;
     EGLContext mEGLContext;
     EGLDisplay mEGLDisplay;
     sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
 
+#ifdef USE_HWC2
+    std::unique_ptr<dvr::VrFlinger> mVrFlinger;
+#endif
+
     // Can only accessed from the main thread, these members
     // don't need synchronization
-    State mDrawingState;
+    State mDrawingState{LayerVector::StateSet::Drawing};
     bool mVisibleRegionsDirty;
 #ifndef USE_HWC2
     bool mHwWorkListDirty;
@@ -509,6 +641,8 @@
     sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
     bool mHadClientComposition = false;
 #endif
+    FenceTimeline mGlCompositionDoneTimeline;
+    FenceTimeline mDisplayTimeline;
 
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
@@ -525,11 +659,14 @@
     nsecs_t mLastTransactionTime;
     bool mBootFinished;
     bool mForceFullDamage;
-    FenceTracker mFenceTracker;
 #ifdef USE_HWC2
     bool mPropagateBackpressure = true;
 #endif
-    bool mUseHwcVirtualDisplays = true;
+    SurfaceInterceptor mInterceptor;
+    bool mUseHwcVirtualDisplays = false;
+
+    // Restrict layers to use two buffers in their bufferqueues.
+    bool mLayerTripleBufferingDisabled = false;
 
     // these are thread safe
     mutable MessageQueue mEventQueue;
@@ -545,10 +682,23 @@
     bool mPrimaryHWVsyncEnabled;
     bool mHWVsyncAvailable;
 
+    // protected by mCompositorTimingLock;
+    mutable std::mutex mCompositorTimingLock;
+    CompositorTiming mCompositorTiming;
+
+    // Only accessed from the main thread.
+    struct CompositePresentTime {
+        nsecs_t composite { -1 };
+        std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE };
+    };
+    std::queue<CompositePresentTime> mCompositePresentTimes;
+
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
 
+    bool mInjectVSyncs;
+
     Daltonizer mDaltonizer;
 #ifndef USE_HWC2
     bool mDaltonize;
@@ -565,6 +715,8 @@
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
 
+    size_t mNumLayers;
+
     // Double- vs. triple-buffering stats
     struct BufferingStats {
         BufferingStats()
@@ -587,8 +739,16 @@
     };
     mutable Mutex mBufferingStatsMutex;
     std::unordered_map<std::string, BufferingStats> mBufferingStats;
-};
 
+    // Verify that transaction is being called by an approved process:
+    // either AID_GRAPHICS or AID_SYSTEM.
+    status_t CheckTransactCodeCredentials(uint32_t code);
+
+#ifdef USE_HWC2
+    std::atomic<bool> mVrFlingerRequestsDisplay;
+    static bool useVrFlinger;
+#endif
+    };
 }; // namespace android
 
 #endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index e0e4c61..abc8fde 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -23,6 +23,7 @@
 #include <private/gui/SyncFeatures.h>
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 
 #include <utils/Errors.h>
 #include <utils/NativeHandle.h>
@@ -70,6 +71,14 @@
         return err;
     }
 
+    if (autoRefresh) {
+        *autoRefresh = item.mAutoRefresh;
+    }
+
+    if (queuedBuffer) {
+        *queuedBuffer = item.mQueuedBuffer;
+    }
+
     // We call the rejecter here, in case the caller has a reason to
     // not accept this buffer.  This is used by SurfaceFlinger to
     // reject buffers which have the wrong size
@@ -79,14 +88,6 @@
         return BUFFER_REJECTED;
     }
 
-    if (autoRefresh) {
-        *autoRefresh = item.mAutoRefresh;
-    }
-
-    if (queuedBuffer) {
-        *queuedBuffer = item.mQueuedBuffer;
-    }
-
     // Release the previous buffer.
 #ifdef USE_HWC2
     err = updateAndReleaseLocked(item, &mPendingRelease);
@@ -138,7 +139,9 @@
 }
 
 sp<NativeHandle> SurfaceFlingerConsumer::getSidebandStream() const {
-    return mConsumer->getSidebandStream();
+    sp<NativeHandle> stream;
+    mConsumer->getSidebandStream(&stream);
+    return stream;
 }
 
 // We need to determine the time when a buffer acquired now will be
@@ -178,21 +181,25 @@
     const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
 
     // The DispSync time is already adjusted for the difference between
-    // vsync and reported-vsync (PRESENT_TIME_OFFSET_FROM_VSYNC_NS), so
+    // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
     // we don't need to factor that in here.  Pad a little to avoid
     // weird effects if apps might be requesting times right on the edge.
     nsecs_t extraPadding = 0;
-    if (VSYNC_EVENT_PHASE_OFFSET_NS == 0) {
+    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
         extraPadding = 1000000;        // 1ms (6% of 60Hz)
     }
 
     return nextRefresh + extraPadding;
 }
 
+sp<Fence> SurfaceFlingerConsumer::getPrevFinalReleaseFence() const {
+    Mutex::Autolock lock(mMutex);
+    return ConsumerBase::mPrevFinalReleaseFence;
+}
+
 #ifdef USE_HWC2
 void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence)
 {
-    mPrevReleaseFence = fence;
     if (!mPendingRelease.isPending) {
         GLConsumer::setReleaseFence(fence);
         return;
@@ -207,32 +214,24 @@
     }
 }
 
-void SurfaceFlingerConsumer::releasePendingBuffer()
+bool SurfaceFlingerConsumer::releasePendingBuffer()
 {
     if (!mPendingRelease.isPending) {
         ALOGV("Pending buffer already released");
-        return;
+        return false;
     }
     ALOGV("Releasing pending buffer");
     Mutex::Autolock lock(mMutex);
     status_t result = releaseBufferLocked(mPendingRelease.currentTexture,
             mPendingRelease.graphicBuffer, mPendingRelease.display,
             mPendingRelease.fence);
-    ALOGE_IF(result != NO_ERROR, "releasePendingBuffer failed: %s (%d)",
+    ALOGE_IF(result < NO_ERROR, "releasePendingBuffer failed: %s (%d)",
             strerror(-result), result);
     mPendingRelease = PendingRelease();
-}
-#else
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) {
-    mPrevReleaseFence = fence;
-    GLConsumer::setReleaseFence(fence);
+    return true;
 }
 #endif
 
-sp<Fence> SurfaceFlingerConsumer::getPrevReleaseFence() const {
-    return mPrevReleaseFence;
-}
-
 void SurfaceFlingerConsumer::setContentsChangedListener(
         const wp<ContentsChangedListener>& listener) {
     setFrameAvailableListener(listener);
@@ -241,10 +240,15 @@
 }
 
 void SurfaceFlingerConsumer::onSidebandStreamChanged() {
+    FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
+    }
     sp<ContentsChangedListener> listener;
     {   // scope for the lock
         Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(mFrameAvailableListener.unsafe_get() == mContentsChangedListener.unsafe_get());
+        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
         listener = mContentsChangedListener.promote();
     }
 
@@ -253,10 +257,20 @@
     }
 }
 
-bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
-        FrameTimestamps* outTimestamps) const {
-    sp<const Layer> l = mLayer.promote();
-    return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+void SurfaceFlingerConsumer::onDisconnect() {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->onDisconnect();
+    }
+}
+
+void SurfaceFlingerConsumer::addAndGetFrameTimestamps(
+        const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta *outDelta) {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+    }
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 4271039..1126233 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -18,6 +18,8 @@
 #define ANDROID_SURFACEFLINGERCONSUMER_H
 
 #include "DispSync.h"
+
+#include <ui/Region.h>
 #include <gui/GLConsumer.h>
 
 namespace android {
@@ -37,10 +39,9 @@
     };
 
     SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
-            uint32_t tex, const Layer* layer)
+            uint32_t tex, Layer* layer)
         : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
-          mTransformToDisplayInverse(false), mSurfaceDamage(),
-          mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
+          mTransformToDisplayInverse(false), mSurfaceDamage(), mLayer(layer)
     {}
 
     class BufferRejecter {
@@ -61,7 +62,7 @@
     // texture.
     status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
             bool* autoRefresh, bool* queuedBuffer,
-            uint64_t maxFrameNumber = 0);
+            uint64_t maxFrameNumber);
 
     // See GLConsumer::bindTextureImageLocked().
     status_t bindTextureImage();
@@ -79,14 +80,16 @@
 
     nsecs_t computeExpectedPresent(const DispSync& dispSync);
 
-    virtual void setReleaseFence(const sp<Fence>& fence) override;
-    sp<Fence> getPrevReleaseFence() const;
+    sp<Fence> getPrevFinalReleaseFence() const;
 #ifdef USE_HWC2
-    void releasePendingBuffer();
+    virtual void setReleaseFence(const sp<Fence>& fence) override;
+    bool releasePendingBuffer();
 #endif
 
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const override;
+    void onDisconnect() override;
+    void addAndGetFrameTimestamps(
+            const NewFrameEventsEntry* newTimestamps,
+            FrameEventHistoryDelta* outDelta) override;
 
 private:
     virtual void onSidebandStreamChanged();
@@ -107,11 +110,8 @@
     PendingRelease mPendingRelease;
 #endif
 
-    // The release fence of the already displayed buffer (previous frame).
-    sp<Fence> mPrevReleaseFence;
-
     // The layer for this SurfaceFlingerConsumer
-    wp<const Layer> mLayer;
+    const wp<Layer> mLayer;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 271930b..0904fab 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -16,33 +16,32 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <dlfcn.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdatomic.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <errno.h>
-#include <math.h>
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
+
+#include <mutex>
 
 #include <EGL/egl.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
 #include <binder/PermissionCache.h>
 
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
 
-#include <gui/BitTube.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/Surface.h>
-#include <gui/GraphicBufferAlloc.h>
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/HdrCapabilities.h>
@@ -70,7 +69,9 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "Layer.h"
+#include "LayerVector.h"
 #include "LayerDim.h"
+#include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/FramebufferSurface.h"
@@ -82,6 +83,9 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -93,40 +97,25 @@
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 namespace android {
-
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
 // ---------------------------------------------------------------------------
 
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
+bool SurfaceFlinger::useContextPriority;
+int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
+bool SurfaceFlinger::useHwcForRgbToYuv;
+uint64_t SurfaceFlinger::maxVirtualDisplaySize;
+bool SurfaceFlinger::hasSyncFramework;
+int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -134,6 +123,7 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
         mRenderEngine(NULL),
         mBootTime(systemTime()),
@@ -150,6 +140,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(this),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -158,11 +149,35 @@
         mHasPoweredOff(false),
         mFrameBuckets(),
         mTotalTime(0),
-        mLastSwapTime(0)
+        mLastSwapTime(0),
+        mNumLayers(0)
 {
     ALOGI("SurfaceFlinger is starting");
 
-    // debugging stuff...
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
+    maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+
+    hasSyncFramework = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+
+    useContextPriority = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useContextPriority>(false);
+
+    dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
+
+    useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
+
+    maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+
     char value[PROPERTY_VALUE_MAX];
 
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -182,9 +197,13 @@
     ALOGI_IF(mDebugRegion, "showupdates enabled");
     ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
 
-    property_get("debug.sf.disable_hwc_vds", value, "0");
-    mUseHwcVirtualDisplays = !atoi(value);
-    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+    property_get("debug.sf.enable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "1");
+    mLayerTripleBufferingDisabled = atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -210,15 +229,29 @@
     startBootAnim();
 }
 
-sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
-{
-    sp<ISurfaceComposerClient> bclient;
-    sp<Client> client(new Client(this));
+static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {
     status_t err = client->initCheck();
     if (err == NO_ERROR) {
-        bclient = client;
+        return client;
     }
-    return bclient;
+    return nullptr;
+}
+
+sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
+    return initClient(new Client(this));
+}
+
+sp<ISurfaceComposerClient> SurfaceFlinger::createScopedConnection(
+        const sp<IGraphicBufferProducer>& gbp) {
+    if (authenticateSurfaceTexture(gbp) == false) {
+        return nullptr;
+    }
+    const auto& layer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+    if (layer == nullptr) {
+        return nullptr;
+    }
+
+   return initClient(new Client(this, layer));
 }
 
 sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
@@ -233,7 +266,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -244,7 +277,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -262,7 +295,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -274,6 +307,7 @@
     // All non-virtual displays are currently considered secure.
     DisplayDeviceState info(type, true);
     mCurrentState.displays.add(mBuiltinDisplays[type], info);
+    mInterceptor.saveDisplayCreation(info);
 }
 
 sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
@@ -284,14 +318,11 @@
     return mBuiltinDisplays[id];
 }
 
-sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc()
-{
-    sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());
-    return gba;
-}
-
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -446,6 +477,30 @@
     bool mEnabled;
 };
 
+class InjectVSyncSource : public VSyncSource {
+public:
+    InjectVSyncSource() {}
+
+    virtual ~InjectVSyncSource() {}
+
+    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        mCallback = callback;
+    }
+
+    virtual void onInjectSyncEvent(nsecs_t when) {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        mCallback->onVSyncEvent(when);
+    }
+
+    virtual void setVSyncEnabled(bool) {}
+    virtual void setPhaseOffset(nsecs_t) {}
+
+private:
+    std::mutex mCallbackMutex; // Protects the following
+    sp<VSyncSource::Callback> mCallback;
+};
+
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
@@ -459,19 +514,21 @@
     // start the EventThread
     sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
             vsyncPhaseOffsetNs, true, "app");
-    mEventThread = new EventThread(vsyncSrc, *this);
+    mEventThread = new EventThread(vsyncSrc, *this, false);
     sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
             sfVsyncPhaseOffsetNs, true, "sf");
-    mSFEventThread = new EventThread(sfVsyncSrc, *this);
+    mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
     mEventQueue.setEventThread(mSFEventThread);
 
-    // set SFEventThread to SCHED_FIFO to minimize jitter
+    // set EventThread and SFEventThread to SCHED_FIFO to minimize jitter
     struct sched_param param = {0};
     param.sched_priority = 2;
     if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, &param) != 0) {
         ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
     }
-
+    if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO for EventThread");
+    }
 
     // Initialize the H/W composer object.  There may or may not be an
     // actual hardware composer underneath.
@@ -487,6 +544,9 @@
     LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
             "couldn't create EGLContext");
 
+    // Inform native graphics APIs that the present timestamp is NOT supported:
+    property_set(kTimestampProperty, "0");
+
     // initialize our non-virtual displays
     for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
         DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
@@ -499,8 +559,7 @@
 
             sp<IGraphicBufferProducer> producer;
             sp<IGraphicBufferConsumer> consumer;
-            BufferQueue::createBufferQueue(&producer, &consumer,
-                    new GraphicBufferAlloc());
+            BufferQueue::createBufferQueue(&producer, &consumer);
 
             sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                     consumer);
@@ -508,7 +567,7 @@
             sp<DisplayDevice> hw = new DisplayDevice(this,
                     type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                     fbs, producer,
-                    mRenderEngine->getEGLConfig());
+                    mRenderEngine->getEGLConfig(), false);
             if (i > DisplayDevice::DISPLAY_PRIMARY) {
                 // FIXME: currently we don't get blank/unblank requests
                 // for displays other than the main display, so we always
@@ -522,7 +581,7 @@
 
     // make the GLContext current so that we can create textures when creating Layers
     // (which may happens before we render something)
-    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
+    getDefaultDisplayDeviceLocked()->makeCurrent(mEGLDisplay, mEGLContext);
 
     mEventControlThread = new EventControlThread(this);
     mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
@@ -540,8 +599,12 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
+
+    ALOGV("Done initializing");
 }
 
 int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) {
@@ -550,9 +613,13 @@
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -568,10 +635,30 @@
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const {
     Mutex::Autolock _l(mStateLock);
+    return authenticateSurfaceTextureLocked(bufferProducer);
+}
+
+bool SurfaceFlinger::authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const {
     sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
     return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
 }
 
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+        std::vector<FrameEvent>* outSupported) const {
+    *outSupported = {
+        FrameEvent::REQUESTED_PRESENT,
+        FrameEvent::ACQUIRE,
+        FrameEvent::LATCH,
+        FrameEvent::FIRST_REFRESH_START,
+        FrameEvent::LAST_REFRESH_START,
+        FrameEvent::GPU_COMPOSITION_DONE,
+        FrameEvent::DEQUEUE_READY,
+        FrameEvent::RELEASE,
+    };
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
         Vector<DisplayInfo>* configs) {
     if ((configs == NULL) || (display.get() == NULL)) {
@@ -640,7 +727,7 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = float(1e9 / hwConfig.refresh);
-        info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+        info.appVsyncOffset = vsyncPhaseOffsetNs;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -655,7 +742,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline =
-                hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                hwConfig.refresh - sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
@@ -680,7 +767,7 @@
 }
 
 int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
-    sp<DisplayDevice> device(getDisplayDevice(display));
+    sp<const DisplayDevice> device(getDisplayDevice(display));
     if (device != NULL) {
         return device->getActiveConfig();
     }
@@ -809,10 +896,49 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+    if (enable == mInjectVSyncs) {
+        return NO_ERROR;
+    }
+
+    if (enable) {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections enabled");
+        if (mVSyncInjector.get() == nullptr) {
+            mVSyncInjector = new InjectVSyncSource();
+            mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+        }
+        mEventQueue.setEventThread(mInjectorEventThread);
+    } else {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections disabled");
+        mEventQueue.setEventThread(mSFEventThread);
+        mVSyncInjector.clear();
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs) {
+        ALOGE("VSync Injections not enabled");
+        return BAD_VALUE;
+    }
+    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+        ALOGV("Injecting VSync inside SurfaceFlinger");
+        mVSyncInjector->onInjectSyncEvent(when);
+    }
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
-sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
-    return mEventThread->createEventConnection();
+sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
+        ISurfaceComposer::VsyncSource vsyncSource) {
+    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+        return mSFEventThread->createEventConnection();
+    } else {
+        return mEventThread->createEventConnection();
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -908,7 +1034,8 @@
     }
 }
 
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
+void SurfaceFlinger::onVSyncReceived(HWComposer* /*composer*/, int type,
+                                     nsecs_t timestamp) {
     bool needsHwVsync = false;
 
     { // Scope for the lock
@@ -925,7 +1052,12 @@
     }
 }
 
-void SurfaceFlinger::onHotplugReceived(int type, bool connected) {
+void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
+    std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+    *compositorTiming = mCompositorTiming;
+}
+
+void SurfaceFlinger::onHotplugReceived(HWComposer* /*composer*/, int type, bool connected) {
     if (mEventThread == NULL) {
         // This is a temporary workaround for b/7145521.  A non-null pointer
         // does not mean EventThread has finished initializing, so this
@@ -948,6 +1080,10 @@
     }
 }
 
+void SurfaceFlinger::onInvalidateReceived(HWComposer* /*composer*/) {
+    repaintEverything();
+}
+
 void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
     ATRACE_CALL();
     getHwComposer().eventControl(disp, event, enabled);
@@ -976,7 +1112,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -994,7 +1130,7 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
@@ -1042,59 +1178,143 @@
     }
 }
 
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
 {
     bool needExtraInvalidate = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (layers[i]->onPreComposition()) {
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        if (layer->onPreComposition(refreshStartTime)) {
             needExtraInvalidate = true;
         }
-    }
+    });
+
     if (needExtraInvalidate) {
         signalLayerUpdate();
     }
 }
 
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-{
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        bool frameLatched = layers[i]->onPostComposition();
-        if (frameLatched) {
-            recordBufferingStats(layers[i]->getName().string(),
-                    layers[i]->getOccupancyHistory(false));
+void SurfaceFlinger::updateCompositorTiming(
+        nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+        std::shared_ptr<FenceTime>& presentFenceTime) {
+    // Update queue of past composite+present times and determine the
+    // most recently known composite to present latency.
+    mCompositePresentTimes.push({compositeTime, presentFenceTime});
+    nsecs_t compositeToPresentLatency = -1;
+    while (!mCompositePresentTimes.empty()) {
+        CompositePresentTime& cpt = mCompositePresentTimes.front();
+        // Cached values should have been updated before calling this method,
+        // which helps avoid duplicate syscalls.
+        nsecs_t displayTime = cpt.display->getCachedSignalTime();
+        if (displayTime == Fence::SIGNAL_TIME_PENDING) {
+            break;
         }
+        compositeToPresentLatency = displayTime - cpt.composite;
+        mCompositePresentTimes.pop();
     }
 
-    const HWComposer& hwc = getHwComposer();
-    sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+    // Don't let mCompositePresentTimes grow unbounded, just in case.
+    while (mCompositePresentTimes.size() > 16) {
+        mCompositePresentTimes.pop();
+    }
 
-    if (presentFence->isValid()) {
-        if (mPrimaryDispSync.addPresentFence(presentFence)) {
+    setCompositorTimingSnapped(
+            vsyncPhase, vsyncInterval, compositeToPresentLatency);
+}
+
+void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase,
+        nsecs_t vsyncInterval, nsecs_t compositeToPresentLatency) {
+    // Integer division and modulo round toward 0 not -inf, so we need to
+    // treat negative and positive offsets differently.
+    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) ?
+            (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
+            ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+
+    // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+    if (idealLatency <= 0) {
+        idealLatency = vsyncInterval;
+    }
+
+    // Snap the latency to a value that removes scheduling jitter from the
+    // composition and present times, which often have >1ms of jitter.
+    // Reducing jitter is important if an app attempts to extrapolate
+    // something (such as user input) to an accurate diasplay time.
+    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // with (presentLatency % interval).
+    nsecs_t bias = vsyncInterval / 2;
+    int64_t extraVsyncs =
+            (compositeToPresentLatency - idealLatency + bias) / vsyncInterval;
+    nsecs_t snappedCompositeToPresentLatency = (extraVsyncs > 0) ?
+            idealLatency + (extraVsyncs * vsyncInterval) : idealLatency;
+
+    std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+    mCompositorTiming.deadline = vsyncPhase - idealLatency;
+    mCompositorTiming.interval = vsyncInterval;
+    mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+}
+
+void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+{
+    const HWComposer& hwc = getHwComposer();
+    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+    std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
+    if (getHwComposer().hasGlesComposition(hw->getHwcDisplayId())) {
+        glCompositionDoneFenceTime =
+                std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+        mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
+    } else {
+        glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+    }
+    mGlCompositionDoneTimeline.updateSignalTimes();
+
+    sp<Fence> retireFence = mHwc->getDisplayFence(HWC_DISPLAY_PRIMARY);
+    auto retireFenceTime = std::make_shared<FenceTime>(retireFence);
+    mDisplayTimeline.push(retireFenceTime);
+    mDisplayTimeline.updateSignalTimes();
+
+    nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
+    nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+
+    // We use the refreshStartTime which might be sampled a little later than
+    // when we started doing work for this frame, but that should be okay
+    // since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(
+        vsyncPhase, vsyncInterval, refreshStartTime, retireFenceTime);
+    CompositorTiming compositorTiming;
+    {
+        std::lock_guard<std::mutex> lock(mCompositorTimingLock);
+        compositorTiming = mCompositorTiming;
+    }
+
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        // TODO(brianderson): The retire fence is incorrectly passed in as the
+        // present fence. Fix this if this file lives on.
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                retireFenceTime, compositorTiming);
+        if (frameLatched) {
+            recordBufferingStats(layer->getName().string(),
+                    layer->getOccupancyHistory(false));
+        }
+    });
+
+    if (retireFence->isValid()) {
+        if (mPrimaryDispSync.addPresentFence(retireFence)) {
             enableHardwareVsync();
         } else {
             disableHardwareVsync(false);
         }
     }
 
-    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
-    if (kIgnorePresentFences) {
+    if (!hasSyncFramework) {
         if (hw->isDisplayOn()) {
             enableHardwareVsync();
         }
     }
 
-    mFenceTracker.addFrame(refreshStartTime, presentFence,
-            hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        if (presentFence->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(presentFence);
+        if (retireFenceTime->isValid()) {
+            mAnimFrameTracker.setActualPresentFence(std::move(retireFenceTime));
         } else {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
@@ -1132,7 +1352,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1141,14 +1360,11 @@
             const Transform& tr(hw->getTransform());
             const Rect bounds(hw->getBounds());
             if (hw->isDisplayOn()) {
-                SurfaceFlinger::computeVisibleRegions(layers,
-                        hw->getLayerStack(), dirtyRegion, opaqueRegion);
+                computeVisibleRegions(hw->getLayerStack(), dirtyRegion,
+                        opaqueRegion);
 
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; i++) {
-                    const sp<Layer>& layer(layers[i]);
-                    const Layer::State& s(layer->getDrawingState());
-                    if (s.layerStack == hw->getLayerStack()) {
+                mDrawingState.traverseInZOrder([&](Layer* layer) {
+                    if (layer->getLayerStack() == hw->getLayerStack()) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -1156,7 +1372,7 @@
                             layersSortedByZ.add(layer);
                         }
                     }
-                }
+                });
             }
             hw->setVisibleLayersSortedByZ(layersSortedByZ);
             hw->undefinedRegion.set(bounds);
@@ -1379,13 +1595,10 @@
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
-    const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
-    const size_t count = currentLayers.size();
-
     // Notify all layers of available frames
-    for (size_t i = 0; i < count; ++i) {
-        currentLayers[i]->notifyAvailableFrames();
-    }
+    mCurrentState.traverseInZOrder([](Layer* layer) {
+        layer->notifyAvailableFrames();
+    });
 
     /*
      * Traversal of the children
@@ -1393,15 +1606,14 @@
      */
 
     if (transactionFlags & eTraversalNeeded) {
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(currentLayers[i]);
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) continue;
+            if (!trFlags) return;
 
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
                 mVisibleRegionsDirty = true;
-        }
+        });
     }
 
     /*
@@ -1431,9 +1643,9 @@
                         // Call makeCurrent() on the primary display so we can
                         // be sure that nothing associated with this display
                         // is current.
-                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice());
+                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
                         defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext);
-                        sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i)));
+                        sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
                         if (hw != NULL)
                             hw->disconnect(getHwComposer());
                         if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
@@ -1453,7 +1665,7 @@
                         // recreating the DisplayDevice, so we just remove it
                         // from the drawing state, so that it get re-added
                         // below.
-                        sp<DisplayDevice> hw(getDisplayDevice(display));
+                        sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
                         if (hw != NULL)
                             hw->disconnect(getHwComposer());
                         mDisplays.removeItem(display);
@@ -1463,7 +1675,7 @@
                         continue;
                     }
 
-                    const sp<DisplayDevice> disp(getDisplayDevice(display));
+                    const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
                     if (disp != NULL) {
                         if (state.layerStack != draw[i].layerStack) {
                             disp->setLayerStack(state.layerStack);
@@ -1492,8 +1704,7 @@
                     sp<IGraphicBufferProducer> producer;
                     sp<IGraphicBufferProducer> bqProducer;
                     sp<IGraphicBufferConsumer> bqConsumer;
-                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer,
-                            new GraphicBufferAlloc());
+                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
 
                     int32_t hwcDisplayId = -1;
                     if (state.isVirtualDisplay()) {
@@ -1513,9 +1724,9 @@
                             ALOGE_IF(status != NO_ERROR,
                                     "Unable to query height (%d)", status);
                             if (mUseHwcVirtualDisplays &&
-                                    (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 ||
-                                    (width <= MAX_VIRTUAL_DISPLAY_DIMENSION &&
-                                     height <= MAX_VIRTUAL_DISPLAY_DIMENSION))) {
+                                    (SurfaceFlinger::maxVirtualDisplaySize == 0 ||
+                                    (width <= static_cast<int>(SurfaceFlinger::maxVirtualDisplaySize) &&
+                                     height <= static_cast<int>(SurfaceFlinger::maxVirtualDisplaySize)))) {
                                 hwcDisplayId = allocateHwcDisplayId(state.type);
                             }
 
@@ -1545,7 +1756,7 @@
                                 state.type, hwcDisplayId,
                                 mHwc->getFormat(hwcDisplayId), state.isSecure,
                                 display, dispSurface, producer,
-                                mRenderEngine->getEGLConfig());
+                                mRenderEngine->getEGLConfig(), false);
                         hw->setLayerStack(state.layerStack);
                         hw->setProjection(state.orientation,
                                 state.viewport, state.frame);
@@ -1588,13 +1799,13 @@
         //
         sp<const DisplayDevice> disp;
         uint32_t currentlayerStack = 0;
-        for (size_t i=0; i<count; i++) {
+        bool first = true;
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
-            const sp<Layer>& layer(currentLayers[i]);
-            uint32_t layerStack = layer->getDrawingState().layerStack;
-            if (i==0 || currentlayerStack != layerStack) {
+            uint32_t layerStack = layer->getLayerStack();
+            if (first || currentlayerStack != layerStack) {
                 currentlayerStack = layerStack;
                 // figure out if this layerstack is mirrored
                 // (more than one display) if so, pick the default display,
@@ -1619,10 +1830,12 @@
                 // could be null when this layer is using a layerStack
                 // that is not visible on any display. Also can occur at
                 // screen off/on times.
-                disp = getDefaultDisplayDevice();
+                disp = getDefaultDisplayDeviceLocked();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1630,9 +1843,9 @@
      * Perform our own transaction if needed
      */
 
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    if (currentLayers.size() > layers.size()) {
-        // layers have been added
+    if (mLayersAdded) {
+        mLayersAdded = false;
+        // Layers have been added.
         mVisibleRegionsDirty = true;
     }
 
@@ -1641,20 +1854,17 @@
     if (mLayersRemoved) {
         mLayersRemoved = false;
         mVisibleRegionsDirty = true;
-        const size_t count = layers.size();
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(layers[i]);
-            if (currentLayers.indexOf(layer) < 0) {
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            if (mLayersPendingRemoval.indexOf(layer) >= 0) {
                 // this layer is not visible anymore
                 // TODO: we could traverse the tree from front to back and
                 //       compute the actual visible region
                 // TODO: we could cache the transformed region
-                const Layer::State& s(layer->getDrawingState());
-                Region visibleReg = s.active.transform.transform(
-                        Region(Rect(s.active.w, s.active.h)));
-                invalidateLayerStack(s.layerStack, visibleReg);
+                Region visibleReg;
+                visibleReg.set(layer->computeScreenBounds());
+                invalidateLayerStack(layer->getLayerStack(), visibleReg);
             }
-        }
+        });
     }
 
     commitTransaction();
@@ -1692,10 +1902,10 @@
 {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
-        for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
-            recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
-                    mLayersPendingRemoval[i]->getOccupancyHistory(true));
-            mLayersPendingRemoval[i]->onRemoved();
+        for (const auto& l : mLayersPendingRemoval) {
+            recordBufferingStats(l->getName().string(),
+                    l->getOccupancyHistory(true));
+            l->onRemoved();
         }
         mLayersPendingRemoval.clear();
     }
@@ -1705,13 +1915,15 @@
     mAnimCompositionPending = mAnimTransactionPending;
 
     mDrawingState = mCurrentState;
+    mDrawingState.traverseInZOrder([](Layer* layer) {
+        layer->commitChildList();
+    });
     mTransactionPending = false;
     mAnimTransactionPending = false;
     mTransactionCV.broadcast();
 }
 
-void SurfaceFlinger::computeVisibleRegions(
-        const LayerVector& currentLayers, uint32_t layerStack,
+void SurfaceFlinger::computeVisibleRegions(uint32_t layerStack,
         Region& outDirtyRegion, Region& outOpaqueRegion)
 {
     ATRACE_CALL();
@@ -1722,16 +1934,13 @@
 
     outDirtyRegion.clear();
 
-    size_t i = currentLayers.size();
-    while (i--) {
-        const sp<Layer>& layer = currentLayers[i];
-
+    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         // start with the whole surface at its current location
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (s.layerStack != layerStack)
-            continue;
+        if (layer->getLayerStack() != layerStack)
+            return;
 
         /*
          * opaqueRegion: area of a surface that is fully opaque.
@@ -1766,12 +1975,12 @@
         // handle hidden surfaces by setting the visible region to empty
         if (CC_LIKELY(layer->isVisible())) {
             const bool translucent = !layer->isOpaque(s);
-            Rect bounds(s.active.transform.transform(layer->computeBounds()));
+            Rect bounds(layer->computeScreenBounds());
             visibleRegion.set(bounds);
+            Transform tr = layer->getTransform();
             if (!visibleRegion.isEmpty()) {
                 // Remove the transparent area from the visible region
                 if (translucent) {
-                    const Transform tr(s.active.transform);
                     if (tr.preserveRects()) {
                         // transform the transparent region
                         transparentRegion = tr.transform(s.activeTransparentRegion);
@@ -1783,7 +1992,7 @@
                 }
 
                 // compute the opaque region
-                const int32_t layerOrientation = s.active.transform.getOrientation();
+                const int32_t layerOrientation = tr.getOrientation();
                 if (s.alpha==255 && !translucent &&
                         ((layerOrientation & Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
@@ -1840,7 +2049,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1857,10 +2066,10 @@
 
 bool SurfaceFlinger::handlePageFlip()
 {
+    nsecs_t latchTime = systemTime();
     Region dirtyRegion;
 
     bool visibleRegions = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
     bool frameQueued = false;
 
     // Store the set of layers that need updates. This set must not change as
@@ -1873,25 +2082,23 @@
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
     Vector<Layer*> layersWithQueuedFrames;
-    for (size_t i = 0, count = layers.size(); i<count ; i++) {
-        const sp<Layer>& layer(layers[i]);
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (layer->hasQueuedFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(mPrimaryDispSync)) {
-                layersWithQueuedFrames.push_back(layer.get());
+                layersWithQueuedFrames.push_back(layer);
             } else {
                 layer->useEmptyDamage();
             }
         } else {
             layer->useEmptyDamage();
         }
-    }
+    });
     for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
         Layer* layer = layersWithQueuedFrames[i];
-        const Region dirty(layer->latchBuffer(visibleRegions));
+        const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
         layer->useSurfaceDamage();
-        const Layer::State& s(layer->getDrawingState());
-        invalidateLayerStack(s.layerStack, dirty);
+        invalidateLayerStack(layer->getLayerStack(), dirty);
     }
 
     mVisibleRegionsDirty |= visibleRegions;
@@ -2063,7 +2270,7 @@
                                 && hasGlesComposition) {
                             // never clear the very first layer since we're
                             // guaranteed the FB is already cleared
-                            layer->clearWithOpenGL(hw, clip);
+                            layer->clearWithOpenGL(hw);
                         }
                         break;
                     }
@@ -2107,16 +2314,28 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
         const sp<IBinder>& handle,
         const sp<IGraphicBufferProducer>& gbc,
-        const sp<Layer>& lbc)
+        const sp<Layer>& lbc,
+        const sp<Layer>& parent)
 {
     // add this layer to the current state list
     {
         Mutex::Autolock _l(mStateLock);
-        if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
+        if (mNumLayers >= MAX_LAYERS) {
             return NO_MEMORY;
         }
-        mCurrentState.layersSortedByZ.add(lbc);
+        if (parent == nullptr) {
+            mCurrentState.layersSortedByZ.add(lbc);
+        } else {
+            if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) {
+                ALOGE("addClientLayer called with a removed parent");
+                return NAME_NOT_FOUND;
+            }
+            parent->addChild(lbc);
+        }
+
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2125,25 +2344,52 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) {
     Mutex::Autolock _l(mStateLock);
-    sp<Layer> layer = weakLayer.promote();
-    if (layer == nullptr) {
-        // The layer has already been removed, carry on
+
+    const auto& p = layer->getParent();
+    ssize_t index;
+    if (p != nullptr) {
+        if (topLevelOnly) {
+            return NO_ERROR;
+        }
+
+        sp<Layer> ancestor = p;
+        while (ancestor->getParent() != nullptr) {
+            ancestor = ancestor->getParent();
+        }
+        if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
+            ALOGE("removeLayer called with a layer whose parent has been removed");
+            return NAME_NOT_FOUND;
+        }
+
+        index = p->removeChild(layer);
+    } else {
+        index = mCurrentState.layersSortedByZ.remove(layer);
+    }
+
+    // As a matter of normal operation, the LayerCleaner will produce a second
+    // attempt to remove the surface. The Layer will be kept alive in mDrawingState
+    // so we will succeed in promoting it, but it's already been removed
+    // from mCurrentState. As long as we can find it in mDrawingState we have no problem
+    // otherwise something has gone wrong and we are leaking the layer.
+    if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
+    } else if (index < 0) {
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
-    }
-    return status_t(index);
+    mLayersPendingRemoval.add(layer);
+    mLayersRemoved = true;
+    mNumLayers -= 1 + layer->getChildrenCount();
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2219,6 +2465,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2310,13 +2560,20 @@
         }
         if (what & layer_state_t::eLayerChanged) {
             // NOTE: index needs to be calculated before we update the state
-            ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-            if (layer->setLayer(s.z) && idx >= 0) {
-                mCurrentState.layersSortedByZ.removeAt(idx);
-                mCurrentState.layersSortedByZ.add(layer);
-                // we need traversal (state changed)
-                // AND transaction (list changed)
-                flags |= eTransactionNeeded|eTraversalNeeded;
+            const auto& p = layer->getParent();
+            if (p == nullptr) {
+                ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+                if (layer->setLayer(s.z) && idx >= 0) {
+                    mCurrentState.layersSortedByZ.removeAt(idx);
+                    mCurrentState.layersSortedByZ.add(layer);
+                    // we need traversal (state changed)
+                    // AND transaction (list changed)
+                    flags |= eTransactionNeeded|eTraversalNeeded;
+                }
+            } else {
+                if (p->setChildLayer(layer, s.z)) {
+                    flags |= eTransactionNeeded|eTraversalNeeded;
+                }
             }
         }
         if (what & layer_state_t::eSizeChanged) {
@@ -2345,13 +2602,21 @@
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eFinalCropChanged) {
-            if (layer->setFinalCrop(s.finalCrop))
+            if (layer->setFinalCrop(s.finalCrop, !geometryAppliesWithResize))
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eLayerStackChanged) {
-            // NOTE: index needs to be calculated before we update the state
             ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-            if (layer->setLayerStack(s.layerStack) && idx >= 0) {
+            // We only allow setting layer stacks for top level layers,
+            // everything else inherits layer stack from its parent.
+            if (layer->hasParent()) {
+                ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
+                        layer->getName().string());
+            } else if (idx < 0) {
+                ALOGE("Attempt to set layer stack on layer without parent (%s) that "
+                        "that also does not appear in the top level layer list. Something"
+                        " has gone wrong.", layer->getName().string());
+            } else if (layer->setLayerStack(s.layerStack)) {
                 mCurrentState.layersSortedByZ.removeAt(idx);
                 mCurrentState.layersSortedByZ.add(layer);
                 // we need traversal (state changed)
@@ -2360,10 +2625,30 @@
             }
         }
         if (what & layer_state_t::eDeferTransaction) {
-            layer->deferTransactionUntil(s.handle, s.frameNumber);
+            if (s.barrierHandle != nullptr) {
+                layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
+            } else if (s.barrierGbp != nullptr) {
+                const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+                if (authenticateSurfaceTextureLocked(gbp)) {
+                    const auto& otherLayer =
+                        (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+                    layer->deferTransactionUntil(otherLayer, s.frameNumber);
+                } else {
+                    ALOGE("Attempt to defer transaction to to an"
+                            " unrecognized GraphicBufferProducer");
+                }
+            }
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eReparentChildren) {
+            if (layer->reparentChildren(s.reparentHandle)) {
+                flags |= eTransactionNeeded|eTraversalNeeded;
+            }
+        }
+        if (what & layer_state_t::eDetachChildren) {
+            layer->detachChildren();
+        }
         if (what & layer_state_t::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2377,9 +2662,9 @@
         const String8& name,
         const sp<Client>& client,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
+        uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
+        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
 {
-    //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -2390,15 +2675,17 @@
 
     sp<Layer> layer;
 
+    String8 uniqueName = getUniqueLayerName(name);
+
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceNormal:
             result = createNormalLayer(client,
-                    name, w, h, flags, format,
+                    uniqueName, w, h, flags, format,
                     handle, gbp, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceDim:
             result = createDimLayer(client,
-                    name, w, h, flags,
+                    uniqueName, w, h, flags,
                     handle, gbp, &layer);
             break;
         default:
@@ -2410,15 +2697,42 @@
         return result;
     }
 
-    result = addClientLayer(client, *handle, *gbp, layer);
+    layer->setInfo(windowType, ownerUid);
+
+    result = addClientLayer(client, *handle, *gbp, layer, *parent);
     if (result != NO_ERROR) {
         return result;
     }
+    mInterceptor.saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
     return result;
 }
 
+String8 SurfaceFlinger::getUniqueLayerName(const String8& name)
+{
+    bool matchFound = true;
+    uint32_t dupeCounter = 0;
+
+    // Tack on our counter whether there is a hit or not, so everyone gets a tag
+    String8 uniqueName = name + "#" + String8(std::to_string(dupeCounter).c_str());
+
+    // Loop over layers until we're sure there is no matching name
+    while (matchFound) {
+        matchFound = false;
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            if (layer->getName() == uniqueName) {
+                matchFound = true;
+                uniqueName = name + "#" + String8(std::to_string(++dupeCounter).c_str());
+            }
+        });
+    }
+
+    ALOGD_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), uniqueName.c_str());
+
+    return uniqueName;
+}
+
 status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
         const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
         sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
@@ -2457,10 +2771,11 @@
 
 status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
 {
-    // called by the window manager when it wants to remove a Layer
+    // called by a client when it wants to remove a Layer
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
     if (l != NULL) {
+        mInterceptor.saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
                 "error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2472,7 +2787,13 @@
 {
     // called by ~LayerCleaner() when all references to the IBinder (handle)
     // are gone
-    return removeLayer(layer);
+    sp<Layer> l = layer.promote();
+    if (l == nullptr) {
+        // The layer has already been removed, carry on
+        return NO_ERROR;
+    }
+    // If we have a parent, then we can continue to live as long as it does.
+    return removeLayer(l, true);
 }
 
 // ---------------------------------------------------------------------------
@@ -2498,13 +2819,17 @@
     const nsecs_t period =
             getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
     mAnimFrameTracker.setDisplayRefreshPeriod(period);
+
+    // Use phase of 0 since phase is not known.
+    // Use latency of 0, which will snap to the ideal latency.
+    setCompositorTimingSnapped(0, period, 0);
 }
 
 void SurfaceFlinger::initializeDisplays() {
     class MessageScreenInitialized : public MessageBase {
         SurfaceFlinger* flinger;
     public:
-        MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+        explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
         virtual bool handler() {
             flinger->onInitializeDisplays();
             return true;
@@ -2532,11 +2857,20 @@
         return;
     }
 
+    if (mInterceptor.isEnabled()) {
+        Mutex::Autolock _l(mStateLock);
+        ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
+        if (idx < 0) {
+            ALOGW("Surface Interceptor SavePowerMode: invalid display token");
+            return;
+        }
+        mInterceptor.savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+    }
+
     if (currentMode == HWC_POWER_MODE_OFF) {
         // Turn on the display
         getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
             mEventThread->onScreenAcquired();
             resyncToHardwareVsync(true);
@@ -2558,8 +2892,7 @@
             ALOGW("Couldn't set SCHED_OTHER on display off");
         }
 
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (type == DisplayDevice::DISPLAY_PRIMARY) {
             disableHardwareVsync(true); // also cancels any in-progress resync
 
             // FIXME: eventthread only knows about the main display right now
@@ -2569,26 +2902,7 @@
         getHwComposer().setPowerMode(type, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
-    } else if (mode == HWC_POWER_MODE_DOZE ||
-               mode == HWC_POWER_MODE_NORMAL) {
-        // Update display while dozing
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
-            resyncToHardwareVsync(true);
-        }
-    } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
-        // Leave display going to doze
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
-            disableHardwareVsync(true); // also cancels any in-progress resync
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
-        }
-        getHwComposer().setPowerMode(type, mode);
     } else {
-        ALOGE("Attempting to set unknown power mode: %d\n", mode);
         getHwComposer().setPowerMode(type, mode);
     }
 }
@@ -2685,9 +2999,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2707,12 +3021,9 @@
 void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */,
         size_t& /* index */, String8& result) const
 {
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         result.appendFormat("%s\n", layer->getName().string());
-    }
+    });
 }
 
 void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
@@ -2731,14 +3042,11 @@
     if (name.isEmpty()) {
         mAnimFrameTracker.dumpStats(result);
     } else {
-        const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-        const size_t count = currentLayers.size();
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<Layer>& layer(currentLayers[i]);
+        mCurrentState.traverseInZOrder([&](Layer* layer) {
             if (name == layer->getName()) {
                 layer->dumpFrameStats(result);
             }
-        }
+        });
     }
 }
 
@@ -2751,14 +3059,11 @@
         index++;
     }
 
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         if (name.isEmpty() || (name == layer->getName())) {
             layer->clearFrameStats();
         }
-    }
+    });
 
     mAnimFrameTracker.clearStats();
 }
@@ -2766,31 +3071,28 @@
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
-    const LayerVector& drawingLayers = mDrawingState.layersSortedByZ;
-    const size_t count = drawingLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(drawingLayers[i]);
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
         layer->logFrameStats();
-    }
+    });
 
     mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
 }
 
-/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
+void SurfaceFlinger::appendSfConfigString(String8& result) const
 {
-    static const char* config =
-            " [sf"
-#ifdef HAS_CONTEXT_PRIORITY
-            " HAS_CONTEXT_PRIORITY"
-#endif
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-            " NEVER_DEFAULT_TO_ASYNC_MODE"
-#endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
-            " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
-            "]";
-    result.append(config);
+    result.append(" [sf");
+    result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
+
+    if (isLayerTripleBufferingDisabled())
+        result.append(" DISABLE_TRIPLE_BUFFERING");
+
+    result.appendFormat(" PRESENT_TIME_OFFSET=%" PRId64, dispSyncPresentTimeOffset);
+    result.appendFormat(" FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
+    result.appendFormat(" MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
+    result.appendFormat(" RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
+    result.appendFormat(" NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
+                        maxFrameBufferAcquiredBuffers);
+    result.append("]");
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2810,6 +3112,16 @@
             NUM_BUCKETS - 1, bucketTimeSec, percent);
 }
 
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+    result.appendFormat("Layer frame timestamps:\n");
+
+    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+    const size_t count = currentLayers.size();
+    for (size_t i=0 ; i<count ; i++) {
+        currentLayers[i]->dumpFrameEvents(result);
+    }
+}
+
 void SurfaceFlinger::recordBufferingStats(const char* layerName,
         std::vector<OccupancyTracker::Segment>&& history) {
     Mutex::Autolock lock(mBufferingStatsMutex);
@@ -2903,8 +3215,8 @@
     result.append("DispSync configuration: ");
     colorizer.reset(result);
     result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
-            "present offset %d ns (refresh %" PRId64 " ns)",
-        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS,
+            "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
+        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, dispSyncPresentTimeOffset,
         mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY));
     result.append("\n");
 
@@ -2918,15 +3230,12 @@
     /*
      * Dump the visible layer list
      */
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
     colorizer.bold(result);
-    result.appendFormat("Visible layers (count = %zu)\n", count);
+    result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(currentLayers[i]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
         layer->dump(result, colorizer);
-    }
+    });
 
     /*
      * Dump Display state
@@ -2949,7 +3258,7 @@
     colorizer.reset(result);
 
     HWComposer& hwc(getHwComposer());
-    sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+    sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
 
     colorizer.bold(result);
     result.appendFormat("EGL implementation : %s\n",
@@ -3025,7 +3334,7 @@
         // Just use the primary display so we have something to return
         dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
     }
-    return getDisplayDevice(dpy)->getVisibleLayersSortedByZ();
+    return getDisplayDeviceLocked(dpy)->getVisibleLayersSortedByZ();
 }
 
 bool SurfaceFlinger::startDdmConnection()
@@ -3052,7 +3361,6 @@
     switch (code) {
         case CREATE_CONNECTION:
         case CREATE_DISPLAY:
-        case SET_TRANSACTION_STATE:
         case BOOT_FINISHED:
         case CLEAR_ANIMATION_FRAME_STATS:
         case GET_ANIMATION_FRAME_STATS:
@@ -3071,6 +3379,17 @@
             }
             break;
         }
+        /*
+         * Calling setTransactionState is safe, because you need to have been
+         * granted a reference to Client* and Handle* to do anything with it.
+         *
+         * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h
+         */
+        case SET_TRANSACTION_STATE:
+        case CREATE_SCOPED_CONNECTION:
+        {
+            break;
+        }
         case CAPTURE_SCREEN:
         {
             // codes that require permission check
@@ -3215,6 +3534,18 @@
                 mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
                 return NO_ERROR;
             }
+            case 1020: { // Layer updates interceptor
+                n = data.readInt32();
+                if (n) {
+                    ALOGV("Interceptor enabled");
+                    mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+                }
+                else{
+                    ALOGV("Interceptor disabled");
+                    mInterceptor.disable();
+                }
+                return NO_ERROR;
+            }
             case 1021: { // Disable HWC virtual displays
                 n = data.readInt32();
                 mUseHwcVirtualDisplays = !n;
@@ -3316,7 +3647,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3350,7 +3681,7 @@
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
 
     if (CC_UNLIKELY(display == 0))
@@ -3391,7 +3722,7 @@
         sp<IGraphicBufferProducer> producer;
         Rect sourceCrop;
         uint32_t reqWidth, reqHeight;
-        uint32_t minLayerZ,maxLayerZ;
+        int32_t minLayerZ,maxLayerZ;
         bool useIdentityTransform;
         Transform::orientation_flags rotation;
         status_t result;
@@ -3401,7 +3732,7 @@
                 const sp<IBinder>& display,
                 const sp<IGraphicBufferProducer>& producer,
                 Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                uint32_t minLayerZ, uint32_t maxLayerZ,
+                int32_t minLayerZ, int32_t maxLayerZ,
                 bool useIdentityTransform,
                 Transform::orientation_flags rotation,
                 bool isLocalScreenshot)
@@ -3418,7 +3749,7 @@
         }
         virtual bool handler() {
             Mutex::Autolock _l(flinger->mStateLock);
-            sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
+            sp<const DisplayDevice> hw(flinger->getDisplayDeviceLocked(display));
             result = flinger->captureScreenImplLocked(hw, producer,
                     sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
                     useIdentityTransform, rotation, isLocalScreenshot);
@@ -3450,7 +3781,7 @@
 void SurfaceFlinger::renderScreenImplLocked(
         const sp<const DisplayDevice>& hw,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation)
 {
     ATRACE_CALL();
@@ -3494,20 +3825,24 @@
     // redraw the screen entirely...
     engine.clearWithColor(0, 0, 0, 1);
 
-    const LayerVector& layers( mDrawingState.layersSortedByZ );
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; ++i) {
-        const sp<Layer>& layer(layers[i]);
-        const Layer::State& state(layer->getDrawingState());
-        if (state.layerStack == hw->getLayerStack()) {
-            if (state.z >= minLayerZ && state.z <= maxLayerZ) {
-                if (layer->isVisible()) {
-                    if (filtering) layer->setFiltering(true);
-                    layer->draw(hw, useIdentityTransform);
-                    if (filtering) layer->setFiltering(false);
-                }
-            }
+    // We loop through the first level of layers without traversing,
+    // as we need to interpret min/max layer Z in the top level Z space.
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        if (layer->getLayerStack() != hw->getLayerStack()) {
+            continue;
         }
+        const Layer::State& state(layer->getDrawingState());
+        if (state.z < minLayerZ || state.z > maxLayerZ) {
+            continue;
+        }
+        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+            if (!layer->isVisible()) {
+                return;
+            }
+            if (filtering) layer->setFiltering(true);
+            layer->draw(hw, useIdentityTransform);
+            if (filtering) layer->setFiltering(false);
+        });
     }
 
     // compositionComplete is needed for older driver
@@ -3520,7 +3855,7 @@
         const sp<const DisplayDevice>& hw,
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-        uint32_t minLayerZ, uint32_t maxLayerZ,
+        int32_t minLayerZ, int32_t maxLayerZ,
         bool useIdentityTransform, Transform::orientation_flags rotation,
         bool isLocalScreenshot)
 {
@@ -3544,16 +3879,16 @@
     reqHeight = (!reqHeight) ? hw_h : reqHeight;
 
     bool secureLayerIsVisible = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i = 0 ; i < count ; ++i) {
-        const sp<Layer>& layer(layers[i]);
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
         const Layer::State& state(layer->getDrawingState());
-        if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ &&
-                state.z <= maxLayerZ && layer->isVisible() &&
-                layer->isSecure()) {
-            secureLayerIsVisible = true;
+        if ((layer->getLayerStack() != hw->getLayerStack()) ||
+                (state.z < minLayerZ || state.z > maxLayerZ)) {
+            continue;
         }
+        layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer *layer) {
+            secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
+                    layer->isSecure());
+        });
     }
 
     if (!isLocalScreenshot && secureLayerIsVisible) {
@@ -3672,13 +4007,8 @@
     return result;
 }
 
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
-        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
-    return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
 void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
-        const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
+        const sp<const DisplayDevice>& hw, int32_t minLayerZ, int32_t maxLayerZ) {
     if (DEBUG_SCREENSHOTS) {
         for (size_t y=0 ; y<h ; y++) {
             uint32_t const * p = (uint32_t const *)vaddr + y*s;
@@ -3689,76 +4019,33 @@
         ALOGE("*** we just took a black screenshot ***\n"
                 "requested minz=%d, maxz=%d, layerStack=%d",
                 minLayerZ, maxLayerZ, hw->getLayerStack());
-        const LayerVector& layers( mDrawingState.layersSortedByZ );
-        const size_t count = layers.size();
-        for (size_t i=0 ; i<count ; ++i) {
-            const sp<Layer>& layer(layers[i]);
+        size_t i = 0;
+        for (const auto& layer : mDrawingState.layersSortedByZ) {
             const Layer::State& state(layer->getDrawingState());
-            const bool visible = (state.layerStack == hw->getLayerStack())
-                                && (state.z >= minLayerZ && state.z <= maxLayerZ)
-                                && (layer->isVisible());
-            ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
-                    visible ? '+' : '-',
-                            i, layer->getName().string(), state.layerStack, state.z,
+            if (layer->getLayerStack() == hw->getLayerStack() && state.z >= minLayerZ &&
+                    state.z <= maxLayerZ) {
+                layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+                    ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
+                            layer->isVisible() ? '+' : '-',
+                            i, layer->getName().string(), layer->getLayerStack(), state.z,
                             layer->isVisible(), state.flags, state.alpha);
+                    i++;
+                });
+            }
         }
     }
 }
 
 // ---------------------------------------------------------------------------
 
-SurfaceFlinger::LayerVector::LayerVector() {
+void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverseInZOrder(stateSet, visitor);
 }
 
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
-    : SortedVector<sp<Layer> >(rhs) {
+void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
 }
 
-int SurfaceFlinger::LayerVector::do_compare(const void* lhs,
-    const void* rhs) const
-{
-    // sort layers per layer-stack, then by z-order and finally by sequence
-    const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs));
-    const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs));
-
-    uint32_t ls = l->getCurrentState().layerStack;
-    uint32_t rs = r->getCurrentState().layerStack;
-    if (ls != rs)
-        return ls - rs;
-
-    uint32_t lz = l->getCurrentState().z;
-    uint32_t rz = r->getCurrentState().z;
-    if (lz != rz)
-        return lz - rz;
-
-    return l->sequence - r->sequence;
-}
-
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
-    : type(DisplayDevice::DISPLAY_ID_INVALID),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
-    DisplayDevice::DisplayType type, bool isSecure)
-    : type(type),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(isSecure) {
-    viewport.makeInvalid();
-    frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
 }; // namespace android
 
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
new file mode 100644
index 0000000..db489b2
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,597 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#undef LOG_TAG
+#define LOG_TAG "SurfaceInterceptor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceInterceptor.h"
+
+#include <fstream>
+
+#include <android-base/file.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
+    :   mFlinger(flinger)
+{
+}
+
+void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+    if (mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    mEnabled = true;
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    saveExistingDisplaysLocked(displays);
+    saveExistingSurfacesLocked(layers);
+}
+
+void SurfaceInterceptor::disable() {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    mEnabled = false;
+    status_t err(writeProtoFileLocked());
+    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
+    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
+    mTrace.Clear();
+}
+
+bool SurfaceInterceptor::isEnabled() {
+    return mEnabled;
+}
+
+void SurfaceInterceptor::saveExistingDisplaysLocked(
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+    // Caveat: The initial snapshot does not capture the power mode of the existing displays
+    ATRACE_CALL();
+    for (size_t i = 0 ; i < displays.size() ; i++) {
+        addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
+        addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
+    }
+}
+
+void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
+    ATRACE_CALL();
+    for (const auto& l : layers) {
+        l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) {
+            addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+            addInitialSurfaceStateLocked(createTraceIncrementLocked(), layer);
+        });
+    }
+}
+
+void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
+    transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+
+    const int32_t layerId(getLayerId(layer));
+    addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
+            layer->mCurrentState.active.transform.ty());
+    addDepthLocked(transaction, layerId, layer->mCurrentState.z);
+    addAlphaLocked(transaction, layerId, layer->mCurrentState.alpha);
+    addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+    addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
+    addCropLocked(transaction, layerId, layer->mCurrentState.crop);
+    if (layer->mCurrentState.barrierLayer != nullptr) {
+        addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(),
+                layer->mCurrentState.frameNumber);
+    }
+    addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
+    addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
+    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+}
+
+void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
+        const DisplayDeviceState& display)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(false);
+    transaction->set_animation(false);
+
+    addDisplaySurfaceLocked(transaction, display.displayId, display.surface);
+    addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack);
+    addDisplaySizeLocked(transaction, display.displayId, display.width, display.height);
+    addDisplayProjectionLocked(transaction, display.displayId, display.orientation,
+            display.viewport, display.frame);
+}
+
+status_t SurfaceInterceptor::writeProtoFileLocked() {
+    ATRACE_CALL();
+    std::string output;
+
+    if (!mTrace.IsInitialized()) {
+        return NOT_ENOUGH_DATA;
+    }
+    if (!mTrace.SerializeToString(&output)) {
+        return PERMISSION_DENIED;
+    }
+    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+        return PERMISSION_DENIED;
+    }
+
+    return NO_ERROR;
+}
+
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) {
+    const sp<const IBinder>& handle(weakHandle.promote());
+    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+    const sp<const Layer> layer(layerHandle->owner.promote());
+    // layer could be a nullptr at this point
+    return layer;
+}
+
+const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
+    return layer->getName().string();
+}
+
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
+    return layer->sequence;
+}
+
+Increment* SurfaceInterceptor::createTraceIncrementLocked() {
+    Increment* increment(mTrace.add_increment());
+    increment->set_time_stamp(systemTime());
+    return increment;
+}
+
+SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
+        int32_t layerId)
+{
+    SurfaceChange* change(transaction->add_surface_change());
+    change->set_id(layerId);
+    return change;
+}
+
+DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
+        int32_t displayId)
+{
+    DisplayChange* dispChange(transaction->add_display_change());
+    dispChange->set_id(displayId);
+    return dispChange;
+}
+
+void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
+    protoRect->set_left(rect.left);
+    protoRect->set_top(rect.top);
+    protoRect->set_right(rect.right);
+    protoRect->set_bottom(rect.bottom);
+}
+
+void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
+        float x, float y)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    PositionChange* posChange(change->mutable_position());
+    posChange->set_x(x);
+    posChange->set_y(y);
+}
+
+void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
+        uint32_t z)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    LayerChange* depthChange(change->mutable_layer());
+    depthChange->set_layer(z);
+}
+
+void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
+        uint32_t h)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    SizeChange* sizeChange(change->mutable_size());
+    sizeChange->set_w(w);
+    sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
+        float alpha)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    AlphaChange* alphaChange(change->mutable_alpha());
+    alphaChange->set_alpha(alpha);
+}
+
+void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
+        const layer_state_t::matrix22_t& matrix)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    MatrixChange* matrixChange(change->mutable_matrix());
+    matrixChange->set_dsdx(matrix.dsdx);
+    matrixChange->set_dtdx(matrix.dtdx);
+    matrixChange->set_dsdy(matrix.dsdy);
+    matrixChange->set_dtdy(matrix.dtdy);
+}
+
+void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
+        int32_t layerId, const Region& transRegion)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
+
+    for (const auto& rect : transRegion) {
+        Rectangle* protoRect(transparentChange->add_region());
+        setProtoRectLocked(protoRect, rect);
+    }
+}
+
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId,
+        uint8_t flags)
+{
+    // There can be multiple flags changed
+    if (flags & layer_state_t::eLayerHidden) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        HiddenFlagChange* flagChange(change->mutable_hidden_flag());
+        flagChange->set_hidden_flag(true);
+    }
+    if (flags & layer_state_t::eLayerOpaque) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
+        flagChange->set_opaque_flag(true);
+    }
+    if (flags & layer_state_t::eLayerSecure) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        SecureFlagChange* flagChange(change->mutable_secure_flag());
+        flagChange->set_secure_flag(true);
+    }
+}
+
+void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
+        uint32_t layerStack)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    LayerStackChange* layerStackChange(change->mutable_layer_stack());
+    layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
+        const Rect& rect)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    CropChange* cropChange(change->mutable_crop());
+    Rectangle* protoRect(cropChange->mutable_rectangle());
+    setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
+        const Rect& rect)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    FinalCropChange* finalCropChange(change->mutable_final_crop());
+    Rectangle* protoRect(finalCropChange->mutable_rectangle());
+    setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+        const sp<const Layer>& layer, uint64_t frameNumber)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    if (layer == nullptr) {
+        ALOGE("An existing layer could not be retrieved with the handle"
+                " for the deferred transaction");
+        return;
+    }
+    DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
+    deferTransaction->set_layer_id(getLayerId(layer));
+    deferTransaction->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
+        int32_t layerId, int32_t overrideScalingMode)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
+    overrideChange->set_override_scaling_mode(overrideScalingMode);
+}
+
+void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
+        const layer_state_t& state)
+{
+    const sp<const Layer> layer(getLayer(state.surface));
+    if (layer == nullptr) {
+        ALOGE("An existing layer could not be retrieved with the surface "
+                "from the layer_state_t surface in the update transaction");
+        return;
+    }
+
+    const int32_t layerId(getLayerId(layer));
+
+    if (state.what & layer_state_t::ePositionChanged) {
+        addPositionLocked(transaction, layerId, state.x, state.y);
+    }
+    if (state.what & layer_state_t::eLayerChanged) {
+        addDepthLocked(transaction, layerId, state.z);
+    }
+    if (state.what & layer_state_t::eSizeChanged) {
+        addSizeLocked(transaction, layerId, state.w, state.h);
+    }
+    if (state.what & layer_state_t::eAlphaChanged) {
+        addAlphaLocked(transaction, layerId, state.alpha);
+    }
+    if (state.what & layer_state_t::eMatrixChanged) {
+        addMatrixLocked(transaction, layerId, state.matrix);
+    }
+    if (state.what & layer_state_t::eTransparentRegionChanged) {
+        addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
+    }
+    if (state.what & layer_state_t::eFlagsChanged) {
+        addFlagsLocked(transaction, layerId, state.flags);
+    }
+    if (state.what & layer_state_t::eLayerStackChanged) {
+        addLayerStackLocked(transaction, layerId, state.layerStack);
+    }
+    if (state.what & layer_state_t::eCropChanged) {
+        addCropLocked(transaction, layerId, state.crop);
+    }
+    if (state.what & layer_state_t::eDeferTransaction) {
+        sp<Layer> otherLayer = nullptr;
+        if (state.barrierHandle != nullptr) {
+            otherLayer = static_cast<Layer::Handle*>(state.barrierHandle.get())->owner.promote();
+        } else if (state.barrierGbp != nullptr) {
+            auto const& gbp = state.barrierGbp;
+            if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
+                otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+            } else {
+                ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
+            }
+        }
+        addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber);
+    }
+    if (state.what & layer_state_t::eFinalCropChanged) {
+        addFinalCropLocked(transaction, layerId, state.finalCrop);
+    }
+    if (state.what & layer_state_t::eOverrideScalingModeChanged) {
+        addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+    }
+}
+
+void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
+        const DisplayState& state, int32_t displayId)
+{
+    if (state.what & DisplayState::eSurfaceChanged) {
+        addDisplaySurfaceLocked(transaction, displayId, state.surface);
+    }
+    if (state.what & DisplayState::eLayerStackChanged) {
+        addDisplayLayerStackLocked(transaction, displayId, state.layerStack);
+    }
+    if (state.what & DisplayState::eDisplaySizeChanged) {
+        addDisplaySizeLocked(transaction, displayId, state.width, state.height);
+    }
+    if (state.what & DisplayState::eDisplayProjectionChanged) {
+        addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport,
+                state.frame);
+    }
+}
+
+void SurfaceInterceptor::addTransactionLocked(Increment* increment,
+        const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
+    transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+    for (const auto& compState: stateUpdates) {
+        addSurfaceChangesLocked(transaction, compState.state);
+    }
+    for (const auto& disp: changedDisplays) {
+        ssize_t dpyIdx = displays.indexOfKey(disp.token);
+        if (dpyIdx >= 0) {
+            const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
+            addDisplayChangesLocked(transaction, disp, dispState.displayId);
+        }
+    }
+}
+
+void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    SurfaceCreation* creation(increment->mutable_surface_creation());
+    creation->set_id(getLayerId(layer));
+    creation->set_name(getLayerName(layer));
+    creation->set_w(layer->mCurrentState.active.w);
+    creation->set_h(layer->mCurrentState.active.h);
+}
+
+void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    SurfaceDeletion* deletion(increment->mutable_surface_deletion());
+    deletion->set_id(getLayerId(layer));
+}
+
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+        uint32_t width, uint32_t height, uint64_t frameNumber)
+{
+    BufferUpdate* update(increment->mutable_buffer_update());
+    update->set_id(getLayerId(layer));
+    update->set_w(width);
+    update->set_h(height);
+    update->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
+    VSyncEvent* event(increment->mutable_vsync_event());
+    event->set_when(timestamp);
+}
+
+void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+        const sp<const IGraphicBufferProducer>& surface)
+{
+    if (surface == nullptr) {
+        return;
+    }
+    uint64_t bufferQueueId = 0;
+    status_t err(surface->getUniqueId(&bufferQueueId));
+    if (err == NO_ERROR) {
+        DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+        DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
+        surfaceChange->set_buffer_queue_id(bufferQueueId);
+        surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
+    }
+    else {
+        ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
+                strerror(-err));
+    }
+}
+
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
+        int32_t displayId, uint32_t layerStack)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
+    layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId,
+        uint32_t w, uint32_t h)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    SizeChange* sizeChange(dispChange->mutable_size());
+    sizeChange->set_w(w);
+    sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
+        int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    ProjectionChange* projectionChange(dispChange->mutable_projection());
+    projectionChange->set_orientation(orientation);
+    Rectangle* viewportRect(projectionChange->mutable_viewport());
+    setProtoRectLocked(viewportRect, viewport);
+    Rectangle* frameRect(projectionChange->mutable_frame());
+    setProtoRectLocked(frameRect, frame);
+}
+
+void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
+        const DisplayDeviceState& info)
+{
+    DisplayCreation* creation(increment->mutable_display_creation());
+    creation->set_id(info.displayId);
+    creation->set_name(info.displayName);
+    creation->set_type(info.type);
+    creation->set_is_secure(info.isSecure);
+}
+
+void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) {
+    DisplayDeletion* deletion(increment->mutable_display_deletion());
+    deletion->set_id(displayId);
+}
+
+void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId,
+        int32_t mode)
+{
+    PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
+    powerModeUpdate->set_id(displayId);
+    powerModeUpdate->set_mode(mode);
+}
+
+void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t flags)
+{
+    if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
+            flags);
+}
+
+void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+        uint32_t height, uint64_t frameNumber)
+{
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+}
+
+void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
+    if (!mEnabled) {
+        return;
+    }
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
+}
+
+void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addDisplayCreationLocked(createTraceIncrementLocked(), info);
+}
+
+void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addDisplayDeletionLocked(createTraceIncrementLocked(), displayId);
+}
+
+void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
+}
+
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
new file mode 100644
index 0000000..30ebcc6
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEINTERCEPTOR_H
+#define ANDROID_SURFACEINTERCEPTOR_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <mutex>
+
+#include <utils/SortedVector.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class BufferItem;
+class Layer;
+class SurfaceFlinger;
+struct DisplayState;
+struct layer_state_t;
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+/*
+ * SurfaceInterceptor intercepts and stores incoming streams of window
+ * properties on SurfaceFlinger.
+ */
+class SurfaceInterceptor {
+public:
+    SurfaceInterceptor(SurfaceFlinger* const flinger);
+    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
+    void enable(const SortedVector<sp<Layer>>& layers,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+    void disable();
+    bool isEnabled();
+
+    // Intercept display and surface transactions
+    void saveTransaction(const Vector<ComposerState>& stateUpdates,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+            const Vector<DisplayState>& changedDisplays, uint32_t flags);
+
+    // Intercept surface data
+    void saveSurfaceCreation(const sp<const Layer>& layer);
+    void saveSurfaceDeletion(const sp<const Layer>& layer);
+    void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+            uint64_t frameNumber);
+
+    // Intercept display data
+    void saveDisplayCreation(const DisplayDeviceState& info);
+    void saveDisplayDeletion(int32_t displayId);
+    void savePowerModeUpdate(int32_t displayId, int32_t mode);
+    void saveVSyncEvent(nsecs_t timestamp);
+
+private:
+    // The creation increments of Surfaces and Displays do not contain enough information to capture
+    // the initial state of each object, so a transaction with all of the missing properties is
+    // performed at the initial snapshot for each display and surface.
+    void saveExistingDisplaysLocked(
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+    void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
+    void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
+    void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
+
+    status_t writeProtoFileLocked();
+    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
+    const std::string getLayerName(const sp<const Layer>& layer);
+    int32_t getLayerId(const sp<const Layer>& layer);
+
+    Increment* createTraceIncrementLocked();
+    void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
+    void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
+    void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+            uint32_t height, uint64_t frameNumber);
+    void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
+    void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
+    void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
+    void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+
+    // Add surface transactions to the trace
+    SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
+    void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
+    void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
+    void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
+    void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
+    void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
+    void addMatrixLocked(Transaction* transaction, int32_t layerId,
+            const layer_state_t::matrix22_t& matrix);
+    void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+            const Region& transRegion);
+    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+    void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+    void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+            const sp<const Layer>& layer, uint64_t frameNumber);
+    void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+            int32_t overrideScalingMode);
+    void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
+    void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+            const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+
+    // Add display transactions to the trace
+    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
+    void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+            const sp<const IGraphicBufferProducer>& surface);
+    void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+            uint32_t layerStack);
+    void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+            uint32_t h);
+    void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+            int32_t orientation, const Rect& viewport, const Rect& frame);
+    void addDisplayChangesLocked(Transaction* transaction,
+            const DisplayState& state, int32_t displayId);
+
+
+    bool mEnabled {false};
+    std::string mOutputFileName {DEFAULT_FILENAME};
+    std::mutex mTraceMutex {};
+    Trace mTrace {};
+    SurfaceFlinger* const mFlinger;
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 66463a0..6640a13 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -22,8 +22,8 @@
 
 #include <ui/Point.h>
 #include <ui/Rect.h>
-#include <ui/vec2.h>
-#include <ui/vec3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
 
 #include <hardware/hardware.h>
 
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 53a63bd..e50f3ce 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -18,17 +18,67 @@
 
 #include <sched.h>
 
+#include <android/frameworks/displayservice/1.0/IDisplayService.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
 #include <cutils/sched_policy.h>
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+#include <displayservice/DisplayService.h>
+#include <hidl/LegacySupport.h>
+#include <configstore/Utils.h>
 #include "GpuService.h"
 #include "SurfaceFlinger.h"
 
 using namespace android;
 
+static status_t startGraphicsAllocatorService() {
+    using android::hardware::graphics::allocator::V2_0::IAllocator;
+
+    status_t result =
+        hardware::registerPassthroughServiceImplementation<IAllocator>();
+    if (result != OK) {
+        ALOGE("could not start graphics allocator service");
+        return result;
+    }
+
+    return OK;
+}
+
+static status_t startHidlServices() {
+    using android::frameworks::displayservice::V1_0::implementation::DisplayService;
+    using android::frameworks::displayservice::V1_0::IDisplayService;
+    using android::hardware::configstore::getBool;
+    using android::hardware::configstore::getBool;
+    using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
+    hardware::configureRpcThreadpool(1 /* maxThreads */,
+            false /* callerWillJoin */);
+
+    status_t err;
+
+    if (getBool<ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+        err = startGraphicsAllocatorService();
+        if (err != OK) {
+           return err;
+        }
+    }
+
+    sp<IDisplayService> displayservice = new DisplayService();
+    err = displayservice->registerAsService();
+
+    if (err != OK) {
+        ALOGE("Could not register IDisplayService service.");
+    }
+
+    return err;
+}
+
 int main(int, char**) {
+    startHidlServices();
+
     signal(SIGPIPE, SIG_IGN);
     // When SF is launched in its own process, limit the number of
     // binder threads to 4.
@@ -45,12 +95,10 @@
 
     set_sched_policy(0, SP_FOREGROUND);
 
-#ifdef ENABLE_CPUSETS
     // Put most SurfaceFlinger threads in the system-background cpuset
     // Keeps us from unnecessarily using big cores
     // Do this after the binder thread pool init
-    set_cpuset_policy(0, SP_SYSTEM);
-#endif
+    if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM);
 
     // initialize before clients can connect
     flinger->init();
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 435aa0c..aea602b 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -1,6 +1,9 @@
 service surfaceflinger /system/bin/surfaceflinger
-    class core
+    class core animation
     user system
     group graphics drmrpc readproc
     onrestart restart zygote
     writepid /dev/stune/foreground/tasks
+    socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
+    socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
+    socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
index 979062e..16041da 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -8,16 +8,27 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := \
-    Transaction_test.cpp \
+   Transaction_test.cpp \
+   Stress_test.cpp \
+   SurfaceInterceptor_test.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libEGL \
-	libGLESv2 \
-	libbinder \
-	libcutils \
-	libgui \
-	libui \
-	libutils \
+    libEGL \
+    libGLESv2 \
+    libbinder \
+    libcutils \
+    libgui \
+    libprotobuf-cpp-full \
+    libui \
+    libutils \
+    libandroid \
+    liblog
+
+LOCAL_STATIC_LIBRARIES := libtrace_proto
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+
+LOCAL_TEST_DATA = SurfaceFlinger_test.filter
 
 # Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
 # to integrate with auto-test framework.
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
new file mode 100644
index 0000000..33dd2f5
--- /dev/null
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <utils/String8.h>
+
+#include <thread>
+#include <functional>
+
+
+namespace android {
+
+TEST(SurfaceFlingerStress, create_and_destroy) {
+    auto do_stress = []() {
+        sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, client->initCheck());
+        for (int j = 0; j < 1000; j++) {
+            auto surf = client->createSurface(String8("t"), 100, 100,
+                    PIXEL_FORMAT_RGBA_8888, 0);
+            ASSERT_TRUE(surf != nullptr);
+            client->destroySurface(surf->getHandle());
+        }
+    };
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread(do_stress));
+    }
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+}
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
new file mode 100644
index 0000000..915b5cd
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -0,0 +1,5 @@
+{
+        "presubmit": {
+            "filter": "LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*" 
+        }
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
new file mode 100644
index 0000000..0cc763c
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+#include <ui/DisplayInfo.h>
+
+#include <fstream>
+#include <random>
+#include <thread>
+
+namespace android {
+
+constexpr int32_t SCALING_UPDATE = 1;
+constexpr uint32_t BUFFER_UPDATES = 18;
+constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
+constexpr uint32_t SIZE_UPDATE = 134;
+constexpr uint32_t STACK_UPDATE = 1;
+constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float POSITION_UPDATE = 121;
+const Rect CROP_UPDATE(16, 16, 32, 32);
+
+const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
+constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
+    ANativeWindow_Buffer outBuffer;
+    sp<Surface> s = sc->getSurface();
+    ASSERT_TRUE(s != nullptr);
+    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+    for (int y = 0; y < outBuffer.height; y++) {
+        for (int x = 0; x < outBuffer.width; x++) {
+            uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
+            pixel[0] = r;
+            pixel[1] = g;
+            pixel[2] = b;
+            pixel[3] = 255;
+        }
+    }
+    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+}
+
+static status_t readProtoFile(Trace* trace) {
+    status_t err = NO_ERROR;
+
+    int fd = open(DEFAULT_FILENAME, O_RDONLY);
+    {
+        google::protobuf::io::FileInputStream f(fd);
+        if (fd && !trace->ParseFromZeroCopyStream(&f)) {
+            err = PERMISSION_DENIED;
+        }
+    }
+    close(fd);
+
+    return err;
+}
+
+static void enableInterceptor() {
+    system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
+}
+
+static void disableInterceptor() {
+    system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
+}
+
+int32_t getSurfaceId(const std::string& surfaceName) {
+    enableInterceptor();
+    disableInterceptor();
+    Trace capturedTrace;
+    readProtoFile(&capturedTrace);
+    int32_t layerId = 0;
+    for (const auto& increment : *capturedTrace.mutable_increment()) {
+        if (increment.increment_case() == increment.kSurfaceCreation) {
+            if (increment.surface_creation().name() == surfaceName) {
+                layerId = increment.surface_creation().id();
+                break;
+            }
+        }
+    }
+    return layerId;
+}
+
+int32_t getDisplayId(const std::string& displayName) {
+    enableInterceptor();
+    disableInterceptor();
+    Trace capturedTrace;
+    readProtoFile(&capturedTrace);
+    int32_t displayId = 0;
+    for (const auto& increment : *capturedTrace.mutable_increment()) {
+        if (increment.increment_case() == increment.kDisplayCreation) {
+            if (increment.display_creation().name() == displayName) {
+                displayId = increment.display_creation().id();
+                break;
+            }
+        }
+    }
+    return displayId;
+}
+
+class SurfaceInterceptorTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+        // Allow SurfaceInterceptor write to /data
+        system("setenforce 0");
+
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+                ISurfaceComposer::eDisplayIdMain));
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+        ssize_t displayWidth = info.w;
+        ssize_t displayHeight = info.h;
+
+        // Background surface
+        mBGSurfaceControl = mComposerClient->createSurface(
+                String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
+                PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mBGSurfaceControl != NULL);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
+
+        SurfaceComposerClient::openGlobalTransaction();
+        mComposerClient->setDisplayLayerStack(display, 0);
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-3));
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+        SurfaceComposerClient::closeGlobalTransaction(true);
+    }
+
+    virtual void TearDown() {
+        mComposerClient->dispose();
+        mBGSurfaceControl.clear();
+        mComposerClient.clear();
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mBGSurfaceControl;
+    int32_t mBGLayerId;
+    // Used to verify creation and destruction of surfaces and displays
+    int32_t mTargetId;
+
+public:
+    void captureTest(void (SurfaceInterceptorTest::* action)(void),
+            bool (SurfaceInterceptorTest::* verification)(Trace *));
+    void captureTest(void (SurfaceInterceptorTest::* action)(void),
+            SurfaceChange::SurfaceChangeCase changeCase);
+    void captureTest(void (SurfaceInterceptorTest::* action)(void),
+            Increment::IncrementCase incrementCase);
+    void runInTransaction(void (SurfaceInterceptorTest::* action)(void), bool intercepted = false);
+
+    // Verification of changes to a surface
+    bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
+    bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
+    bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
+    bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
+    bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
+    bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop);
+    bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
+    bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
+    bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
+    bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
+    bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
+    bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
+    bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
+    bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
+    bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase);
+    void assertAllUpdatesFound(Trace* trace);
+
+    // Verification of creation and deletion of a surface
+    bool surfaceCreationFound(const Increment& increment, bool foundSurface);
+    bool surfaceDeletionFound(const Increment& increment, bool foundSurface);
+    bool displayCreationFound(const Increment& increment, bool foundDisplay);
+    bool displayDeletionFound(const Increment& increment, bool foundDisplay);
+    bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase);
+
+    // Verification of buffer updates
+    bool bufferUpdatesFound(Trace* trace);
+
+    // Perform each of the possible changes to a surface
+    void positionUpdate();
+    void sizeUpdate();
+    void alphaUpdate();
+    void layerUpdate();
+    void cropUpdate();
+    void finalCropUpdate();
+    void matrixUpdate();
+    void overrideScalingModeUpdate();
+    void transparentRegionHintUpdate();
+    void layerStackUpdate();
+    void hiddenFlagUpdate();
+    void opaqueFlagUpdate();
+    void secureFlagUpdate();
+    void deferredTransactionUpdate();
+    void runAllUpdates();
+    void surfaceCreation();
+    void nBufferUpdates();
+    void displayCreation();
+    void displayDeletion();
+};
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+        bool (SurfaceInterceptorTest::* verification)(Trace *))
+{
+    runInTransaction(action, true);
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    ASSERT_TRUE((this->*verification)(&capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+        Increment::IncrementCase incrementCase)
+{
+    runInTransaction(action, true);
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+        SurfaceChange::SurfaceChangeCase changeCase)
+{
+    runInTransaction(action, true);
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(void),
+        bool intercepted)
+{
+    if (intercepted) {
+        enableInterceptor();
+    }
+    SurfaceComposerClient::openGlobalTransaction();
+    (this->*action)();
+    SurfaceComposerClient::closeGlobalTransaction(true);
+    if (intercepted) {
+        disableInterceptor();
+    }
+}
+
+void SurfaceInterceptorTest::positionUpdate() {
+    mBGSurfaceControl->setPosition(POSITION_UPDATE, POSITION_UPDATE);
+}
+
+void SurfaceInterceptorTest::sizeUpdate() {
+    mBGSurfaceControl->setSize(SIZE_UPDATE, SIZE_UPDATE);
+}
+
+void SurfaceInterceptorTest::alphaUpdate() {
+    mBGSurfaceControl->setAlpha(ALPHA_UPDATE);
+}
+
+void SurfaceInterceptorTest::layerUpdate() {
+    mBGSurfaceControl->setLayer(LAYER_UPDATE);
+}
+
+void SurfaceInterceptorTest::cropUpdate() {
+    mBGSurfaceControl->setCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::finalCropUpdate() {
+    mBGSurfaceControl->setFinalCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::matrixUpdate() {
+    mBGSurfaceControl->setMatrix(M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
+}
+
+void SurfaceInterceptorTest::overrideScalingModeUpdate() {
+    mBGSurfaceControl->setOverrideScalingMode(SCALING_UPDATE);
+}
+
+void SurfaceInterceptorTest::transparentRegionHintUpdate() {
+    Region region(CROP_UPDATE);
+    mBGSurfaceControl->setTransparentRegionHint(region);
+}
+
+void SurfaceInterceptorTest::layerStackUpdate() {
+    mBGSurfaceControl->setLayerStack(STACK_UPDATE);
+}
+
+void SurfaceInterceptorTest::hiddenFlagUpdate() {
+    mBGSurfaceControl->setFlags(layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+}
+
+void SurfaceInterceptorTest::opaqueFlagUpdate() {
+    mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+}
+
+void SurfaceInterceptorTest::secureFlagUpdate() {
+    mBGSurfaceControl->setFlags(layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+}
+
+void SurfaceInterceptorTest::deferredTransactionUpdate() {
+    mBGSurfaceControl->deferTransactionUntil(mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+}
+
+void SurfaceInterceptorTest::displayCreation() {
+    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+    SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::displayDeletion() {
+    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+    mTargetId = getDisplayId(DISPLAY_NAME.string());
+    SurfaceComposerClient::destroyDisplay(testDisplay);
+}
+
+void SurfaceInterceptorTest::runAllUpdates() {
+    runInTransaction(&SurfaceInterceptorTest::positionUpdate);
+    runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
+    runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+    runInTransaction(&SurfaceInterceptorTest::layerUpdate);
+    runInTransaction(&SurfaceInterceptorTest::cropUpdate);
+    runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
+    runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
+    runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
+    runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
+    runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
+    runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
+    runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
+    runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
+    runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+}
+
+void SurfaceInterceptorTest::surfaceCreation() {
+    mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
+            PIXEL_FORMAT_RGBA_8888, 0);
+}
+
+void SurfaceInterceptorTest::nBufferUpdates() {
+    std::random_device rd;
+    std::mt19937_64 gen(rd());
+    // This makes testing fun
+    std::uniform_int_distribution<uint8_t> dis;
+    for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
+        fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
+    }
+}
+
+bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
+    // There should only be one position transaction with x and y = POSITION_UPDATE
+    bool hasX(change.position().x() == POSITION_UPDATE);
+    bool hasY(change.position().y() == POSITION_UPDATE);
+    if (hasX && hasY && !foundPosition) {
+        foundPosition = true;
+    }
+    // Failed because the position update was found a second time
+    else if (hasX && hasY && foundPosition) {
+        [] () { FAIL(); }();
+    }
+    return foundPosition;
+}
+
+bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
+    bool hasWidth(change.size().h() == SIZE_UPDATE);
+    bool hasHeight(change.size().w() == SIZE_UPDATE);
+    if (hasWidth && hasHeight && !foundSize) {
+        foundSize = true;
+    }
+    else if (hasWidth && hasHeight && foundSize) {
+        [] () { FAIL(); }();
+    }
+    return foundSize;
+}
+
+bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
+    bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
+    if (hasAlpha && !foundAlpha) {
+        foundAlpha = true;
+    }
+    else if (hasAlpha && foundAlpha) {
+        [] () { FAIL(); }();
+    }
+    return foundAlpha;
+}
+
+bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
+    bool hasLayer(change.layer().layer() == LAYER_UPDATE);
+    if (hasLayer && !foundLayer) {
+        foundLayer = true;
+    }
+    else if (hasLayer && foundLayer) {
+        [] () { FAIL(); }();
+    }
+    return foundLayer;
+}
+
+bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
+    bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
+    bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
+    bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
+    bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
+    if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
+        foundCrop = true;
+    }
+    else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+        [] () { FAIL(); }();
+    }
+    return foundCrop;
+}
+
+bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change,
+        bool foundFinalCrop)
+{
+    bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
+    bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
+    bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
+    bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
+    if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
+        foundFinalCrop = true;
+    }
+    else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
+        [] () { FAIL(); }();
+    }
+    return foundFinalCrop;
+}
+
+bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
+    bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
+    bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
+    bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
+    bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+    if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
+        foundMatrix = true;
+    }
+    else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+        [] () { FAIL(); }();
+    }
+    return foundMatrix;
+}
+
+bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
+        bool foundScalingMode)
+{
+    bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
+    if (hasScalingUpdate && !foundScalingMode) {
+        foundScalingMode = true;
+    }
+    else if (hasScalingUpdate && foundScalingMode) {
+        [] () { FAIL(); }();
+    }
+    return foundScalingMode;
+}
+
+bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
+        bool foundTransparentRegion)
+{
+    auto traceRegion = change.transparent_region_hint().region(0);
+    bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
+    bool hasTop(traceRegion.top() == CROP_UPDATE.top);
+    bool hasRight(traceRegion.right() == CROP_UPDATE.right);
+    bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
+    if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
+        foundTransparentRegion = true;
+    }
+    else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+        [] () { FAIL(); }();
+    }
+    return foundTransparentRegion;
+}
+
+bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
+        bool foundLayerStack)
+{
+    bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
+    if (hasLayerStackUpdate && !foundLayerStack) {
+        foundLayerStack = true;
+    }
+    else if (hasLayerStackUpdate && foundLayerStack) {
+        [] () { FAIL(); }();
+    }
+    return foundLayerStack;
+}
+
+bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
+        bool foundHiddenFlag)
+{
+    bool hasHiddenFlag(change.hidden_flag().hidden_flag());
+    if (hasHiddenFlag && !foundHiddenFlag) {
+        foundHiddenFlag = true;
+    }
+    else if (hasHiddenFlag && foundHiddenFlag) {
+        [] () { FAIL(); }();
+    }
+    return foundHiddenFlag;
+}
+
+bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
+        bool foundOpaqueFlag)
+{
+    bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
+    if (hasOpaqueFlag && !foundOpaqueFlag) {
+        foundOpaqueFlag = true;
+    }
+    else if (hasOpaqueFlag && foundOpaqueFlag) {
+        [] () { FAIL(); }();
+    }
+    return foundOpaqueFlag;
+}
+
+bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
+        bool foundSecureFlag)
+{
+    bool hasSecureFlag(change.secure_flag().secure_flag());
+    if (hasSecureFlag && !foundSecureFlag) {
+        foundSecureFlag = true;
+    }
+    else if (hasSecureFlag && foundSecureFlag) {
+        [] () { FAIL(); }();
+    }
+    return foundSecureFlag;
+}
+
+bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
+        bool foundDeferred)
+{
+    bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
+    bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
+    if (hasId && hasFrameNumber && !foundDeferred) {
+        foundDeferred = true;
+    }
+    else if (hasId && hasFrameNumber && foundDeferred) {
+        [] () { FAIL(); }();
+    }
+    return foundDeferred;
+}
+
+bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace,
+        SurfaceChange::SurfaceChangeCase changeCase)
+{
+    bool foundUpdate = false;
+    for (const auto& increment : *trace->mutable_increment()) {
+        if (increment.increment_case() == increment.kTransaction) {
+            for (const auto& change : increment.transaction().surface_change()) {
+                if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
+                    switch (changeCase) {
+                        case SurfaceChange::SurfaceChangeCase::kPosition:
+                            // foundUpdate is sent for the tests to fail on duplicated increments
+                            foundUpdate = positionUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kSize:
+                            foundUpdate = sizeUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kAlpha:
+                            foundUpdate = alphaUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kLayer:
+                            foundUpdate = layerUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kCrop:
+                            foundUpdate = cropUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+                            foundUpdate = finalCropUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kMatrix:
+                            foundUpdate = matrixUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+                            foundUpdate = scalingModeUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+                            foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kLayerStack:
+                            foundUpdate = layerStackUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+                            foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+                            foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+                            foundUpdate = secureFlagUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+                            foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
+                            break;
+                    }
+                }
+            }
+        }
+    }
+    return foundUpdate;
+}
+
+void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
+}
+
+bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
+    bool isMatch(increment.surface_creation().name() == LAYER_NAME &&
+            increment.surface_creation().w() == SIZE_UPDATE &&
+            increment.surface_creation().h() == SIZE_UPDATE);
+    if (isMatch && !foundSurface) {
+        foundSurface = true;
+    }
+    else if (isMatch && foundSurface) {
+        [] () { FAIL(); }();
+    }
+    return foundSurface;
+}
+
+bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) {
+    bool isMatch(increment.surface_deletion().id() == mTargetId);
+    if (isMatch && !foundSurface) {
+        foundSurface = true;
+    }
+    else if (isMatch && foundSurface) {
+        [] () { FAIL(); }();
+    }
+    return foundSurface;
+}
+
+bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
+    bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
+            increment.display_creation().is_secure());
+    if (isMatch && !foundDisplay) {
+        foundDisplay = true;
+    }
+    else if (isMatch && foundDisplay) {
+        [] () { FAIL(); }();
+    }
+    return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) {
+    bool isMatch(increment.display_deletion().id() == mTargetId);
+    if (isMatch && !foundDisplay) {
+        foundDisplay = true;
+    }
+    else if (isMatch && foundDisplay) {
+        [] () { FAIL(); }();
+    }
+    return foundDisplay;
+}
+
+bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace,
+        Increment::IncrementCase incrementCase)
+{
+    bool foundIncrement = false;
+    for (const auto& increment : *trace->mutable_increment()) {
+        if (increment.increment_case() == incrementCase) {
+            switch (incrementCase) {
+                case Increment::IncrementCase::kSurfaceCreation:
+                    foundIncrement = surfaceCreationFound(increment, foundIncrement);
+                    break;
+                case Increment::IncrementCase::kSurfaceDeletion:
+                    foundIncrement = surfaceDeletionFound(increment, foundIncrement);
+                    break;
+                case Increment::IncrementCase::kDisplayCreation:
+                    foundIncrement = displayCreationFound(increment, foundIncrement);
+                    break;
+                case Increment::IncrementCase::kDisplayDeletion:
+                    foundIncrement = displayDeletionFound(increment, foundIncrement);
+                    break;
+                default:
+                    /* code */
+                    break;
+            }
+        }
+    }
+    return foundIncrement;
+}
+
+bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+    uint32_t updates = 0;
+    for (const auto& inc : *trace->mutable_increment()) {
+        if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
+            updates++;
+        }
+    }
+    return updates == BUFFER_UPDATES;
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::positionUpdate,
+            SurfaceChange::SurfaceChangeCase::kPosition);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::finalCropUpdate,
+            SurfaceChange::SurfaceChangeCase::kFinalCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
+            SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
+            SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::layerStackUpdate,
+            SurfaceChange::SurfaceChangeCase::kLayerStack);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
+            SurfaceChange::SurfaceChangeCase::kHiddenFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
+            SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
+            SurfaceChange::SurfaceChangeCase::kSecureFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
+            SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
+    enableInterceptor();
+    runAllUpdates();
+    disableInterceptor();
+
+    // Find all of the updates in the single trace
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    assertAllUpdatesFound(&capturedTrace);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
+    captureTest(&SurfaceInterceptorTest::surfaceCreation,
+            Increment::IncrementCase::kSurfaceCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) {
+    sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
+            SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
+    this->mTargetId = getSurfaceId(LAYER_NAME);
+    enableInterceptor();
+    mComposerClient->destroySurface(layerToDelete->getHandle());
+    disableInterceptor();
+
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion));
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
+    captureTest(&SurfaceInterceptorTest::displayCreation,
+            Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
+    captureTest(&SurfaceInterceptorTest::displayDeletion,
+            Increment::IncrementCase::kDisplayDeletion);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+            &SurfaceInterceptorTest::bufferUpdatesFound);
+}
+
+// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
+// first create a snapshot of the existing displays and surfaces and then start capturing
+// the buffer updates
+TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+    enableInterceptor();
+    disableInterceptor();
+    bufferUpdates.join();
+
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    const auto& firstIncrement = capturedTrace.mutable_increment(0);
+    ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
+    enableInterceptor();
+    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+    std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
+    runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
+    bufferUpdates.join();
+    surfaceUpdates.join();
+    disableInterceptor();
+
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+
+    assertAllUpdatesFound(&capturedTrace);
+    ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+    ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation));
+}
+
+}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index f8d4d13..b7792c7 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -18,8 +18,6 @@
 
 #include <android/native_window.h>
 
-#include <binder/IMemory.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -35,7 +33,7 @@
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc,
-        uint8_t r, uint8_t g, uint8_t b) {
+        uint8_t r, uint8_t g, uint8_t b, bool unlock=true) {
     ANativeWindow_Buffer outBuffer;
     sp<Surface> s = sc->getSurface();
     ASSERT_TRUE(s != NULL);
@@ -50,7 +48,9 @@
             pixel[3] = 255;
         }
     }
-    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+    if (unlock) {
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+    }
 }
 
 // A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
@@ -61,7 +61,6 @@
         sp<IGraphicBufferProducer> producer;
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&producer, &consumer);
-        IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(
@@ -85,6 +84,18 @@
         }
     }
 
+    void expectFGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 195, 63, 63);
+    }
+
+    void expectBGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 63, 63, 195);
+    }
+
+    void expectChildColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 200, 200, 200);
+    }
+
 private:
     ScreenCapture(const sp<CpuConsumer>& cc) :
         mCC(cc) {
@@ -141,14 +152,14 @@
 
         mComposerClient->setDisplayLayerStack(display, 0);
 
-        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2));
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX-2));
         ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2,
                 displayHeight-2));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show());
@@ -188,9 +199,9 @@
     {
         SCOPED_TRACE("before move");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -200,9 +211,9 @@
         // This should reflect the new position, but not the new color.
         SCOPED_TRACE("after move, before redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145, 195,  63,  63);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectFGColor(145, 145);
     }
 
     fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
@@ -211,9 +222,9 @@
         // This should reflect the new position and the new color.
         SCOPED_TRACE("after redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63, 195,  63);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->checkPixel(145, 145, 63, 195, 63);
     }
 }
 
@@ -222,9 +233,9 @@
     {
         SCOPED_TRACE("before resize");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     ALOGD("resizing");
@@ -237,9 +248,9 @@
         // has not yet received a buffer of the correct size.
         SCOPED_TRACE("after resize, before redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     ALOGD("drawing");
@@ -250,9 +261,9 @@
         // This should reflect the new size and the new color.
         SCOPED_TRACE("after redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63, 195,  63);
-        sc->checkPixel(145, 145,  63, 195,  63);
+        sc->expectBGColor(24, 24);
+        sc->checkPixel(75, 75, 63, 195, 63);
+        sc->checkPixel(145, 145, 63, 195, 63);
     }
 }
 
@@ -261,9 +272,9 @@
     {
         SCOPED_TRACE("before crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -274,11 +285,11 @@
         // This should crop the foreground surface.
         SCOPED_TRACE("after crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel( 95,  80, 195,  63,  63);
-        sc->checkPixel( 80,  95, 195,  63,  63);
-        sc->checkPixel( 96,  96,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectFGColor(95, 80);
+        sc->expectFGColor(80, 95);
+        sc->expectBGColor(96, 96);
     }
 }
 
@@ -287,9 +298,9 @@
     {
         SCOPED_TRACE("before crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
     SurfaceComposerClient::openGlobalTransaction();
     Rect cropRect(16, 16, 32, 32);
@@ -299,11 +310,11 @@
         // This should crop the foreground surface.
         SCOPED_TRACE("after crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel( 95,  80,  63,  63, 195);
-        sc->checkPixel( 80,  95,  63,  63, 195);
-        sc->checkPixel( 96,  96,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(95, 80);
+        sc->expectBGColor(80, 95);
+        sc->expectBGColor(96, 96);
     }
 }
 
@@ -312,9 +323,9 @@
     {
         SCOPED_TRACE("before setLayer");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -324,9 +335,9 @@
         // This should hide the foreground surface beneath the background.
         SCOPED_TRACE("after setLayer");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -335,9 +346,9 @@
     {
         SCOPED_TRACE("before hide");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -347,9 +358,9 @@
         // This should hide the foreground surface.
         SCOPED_TRACE("after hide, before show");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -359,9 +370,9 @@
         // This should show the foreground surface.
         SCOPED_TRACE("after show");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -370,9 +381,9 @@
     {
         SCOPED_TRACE("before setAlpha");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -382,9 +393,9 @@
         // This should set foreground to be 75% opaque.
         SCOPED_TRACE("after setAlpha");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 162,  63,  96);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->checkPixel(75, 75, 162, 63, 96);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -393,9 +404,9 @@
     {
         SCOPED_TRACE("before setLayerStack");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -406,9 +417,9 @@
         // layer stack.
         SCOPED_TRACE("after setLayerStack");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -417,9 +428,9 @@
     {
         SCOPED_TRACE("before setFlags");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -430,9 +441,9 @@
         // This should hide the foreground surface
         SCOPED_TRACE("after setFlags");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -441,10 +452,10 @@
     {
         SCOPED_TRACE("before setMatrix");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 91,  96, 195,  63,  63);
-        sc->checkPixel( 96, 101, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(91, 96);
+        sc->expectFGColor(96, 101);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -454,21 +465,226 @@
     {
         SCOPED_TRACE("after setMatrix");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 91,  96, 195,  63,  63);
-        sc->checkPixel( 96,  91,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(91, 96);
+        sc->expectBGColor(96, 91);
+        sc->expectBGColor(145, 145);
     }
 }
 
+class GeometryLatchingTest : public LayerUpdateTest {
+protected:
+    void EXPECT_INITIAL_STATE(const char * trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // We find the leading edge of the FG surface.
+        sc->expectFGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+
+    void lockAndFillFGBuffer() {
+        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false);
+    }
+
+    void unlockFGBuffer() {
+        sp<Surface> s = mFGSurfaceControl->getSurface();
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        waitForPostedBuffers();
+    }
+
+    void completeFGResize() {
+        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+        waitForPostedBuffers();
+    }
+    void restoreInitialState() {
+        SurfaceComposerClient::openGlobalTransaction();
+        mFGSurfaceControl->setSize(64, 64);
+        mFGSurfaceControl->setPosition(64, 64);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64));
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+        SurfaceComposerClient::closeGlobalTransaction(true);
+
+        EXPECT_INITIAL_STATE("After restoring initial state");
+    }
+    sp<ScreenCapture> sc;
+};
+
+TEST_F(GeometryLatchingTest, SurfacePositionLatching) {
+    EXPECT_INITIAL_STATE("before anything");
+
+    // By default position can be updated even while
+    // a resize is pending.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(32, 32);
+    mFGSurfaceControl->setPosition(100, 100);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        SCOPED_TRACE("After moving surface");
+        ScreenCapture::captureScreen(&sc);
+        // If we moved, the FG Surface should cover up what was previously BG
+        // however if we didn't move the FG wouldn't be large enough now.
+        sc->expectFGColor(163, 163);
+    }
+
+    restoreInitialState();
+
+    // Now we repeat with setGeometryAppliesWithResize
+    // and verify the position DOESN'T latch.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setGeometryAppliesWithResize();
+    mFGSurfaceControl->setSize(32, 32);
+    mFGSurfaceControl->setPosition(100, 100);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        SCOPED_TRACE("While resize is pending");
+        ScreenCapture::captureScreen(&sc);
+        // This time we shouldn't have moved, so the BG color
+        // should still be visible.
+        sc->expectBGColor(128, 128);
+    }
+
+    completeFGResize();
+
+    {
+        SCOPED_TRACE("After the resize");
+        ScreenCapture::captureScreen(&sc);
+        // But after the resize completes, we should move
+        // and the FG should be visible here.
+        sc->expectFGColor(128, 128);
+    }
+}
+
+class CropLatchingTest : public GeometryLatchingTest {
+protected:
+    void EXPECT_CROPPED_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // The edge should be moved back one pixel by our crop.
+        sc->expectFGColor(126, 126);
+        sc->expectBGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+};
+
+TEST_F(CropLatchingTest, CropLatching) {
+    EXPECT_INITIAL_STATE("before anything");
+    // Normally the crop applies immediately even while a resize is pending.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)");
+
+    restoreInitialState();
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setGeometryAppliesWithResize();
+    mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)");
+
+    completeFGResize();
+
+    EXPECT_CROPPED_STATE("after the resize finishes");
+}
+
+TEST_F(CropLatchingTest, FinalCropLatching) {
+    EXPECT_INITIAL_STATE("before anything");
+    // Normally the crop applies immediately even while a resize is pending.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)");
+
+    restoreInitialState();
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setGeometryAppliesWithResize();
+    mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)");
+
+    completeFGResize();
+
+    EXPECT_CROPPED_STATE("after the resize finishes");
+}
+
+// In this test we ensure that setGeometryAppliesWithResize actually demands
+// a buffer of the new size, and not just any size.
+TEST_F(CropLatchingTest, FinalCropLatchingBufferOldSize) {
+    EXPECT_INITIAL_STATE("before anything");
+    // Normally the crop applies immediately even while a resize is pending.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)");
+
+    restoreInitialState();
+
+    // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
+    // initiating the resize.
+    lockAndFillFGBuffer();
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setGeometryAppliesWithResize();
+    mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)");
+
+    // We now submit our old buffer, at the old size, and ensure it doesn't
+    // trigger geometry latching.
+    unlockFGBuffer();
+
+    EXPECT_INITIAL_STATE("after unlocking FG buffer (with geometryAppliesWithResize)");
+
+    completeFGResize();
+
+    EXPECT_CROPPED_STATE("after the resize finishes");
+}
+
+TEST_F(CropLatchingTest, FinalCropLatchingRegressionForb37531386) {
+    EXPECT_INITIAL_STATE("before anything");
+    // In this scenario, we attempt to set the final crop a second time while the resize
+    // is still pending, and ensure we are successful. Success meaning the second crop
+    // is the one which eventually latches and not the first.
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setSize(128, 128);
+    mFGSurfaceControl->setGeometryAppliesWithResize();
+    mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    EXPECT_INITIAL_STATE("after setting crops with geometryAppliesWithResize");
+
+    completeFGResize();
+
+    EXPECT_INITIAL_STATE("after the resize finishes");
+}
+
 TEST_F(LayerUpdateTest, DeferredTransactionTest) {
     sp<ScreenCapture> sc;
     {
         SCOPED_TRACE("before anything");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 195,  63,  63);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // set up two deferred transactions on different frames
@@ -487,9 +703,9 @@
     {
         SCOPED_TRACE("before any trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 195,  63,  63);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // should trigger the first deferred transaction, but not the second one
@@ -497,9 +713,9 @@
     {
         SCOPED_TRACE("after first trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 162,  63,  96);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->checkPixel(96, 96, 162, 63, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // should show up immediately since it's not deferred
@@ -512,10 +728,388 @@
     {
         SCOPED_TRACE("after second trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96,  63,  63, 195);
-        sc->checkPixel(160, 160, 195,  63,  63);
+        sc->expectBGColor(32, 32);
+        sc->expectBGColor(96, 96);
+        sc->expectFGColor(160, 160);
     }
 }
 
+TEST_F(LayerUpdateTest, LayerSetRelativeLayerWorks) {
+    sp<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before adding relative surface");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
+    }
+
+    auto relativeSurfaceControl = mComposerClient->createSurface(
+            String8("Test Surface"), 64, 64, PIXEL_FORMAT_RGBA_8888, 0);
+    fillSurfaceRGBA8(relativeSurfaceControl, 255, 177, 177);
+    waitForPostedBuffers();
+
+    // Now we stack the surface above the foreground surface and make sure it is visible.
+    SurfaceComposerClient::openGlobalTransaction();
+    relativeSurfaceControl->setPosition(64, 64);
+    relativeSurfaceControl->show();
+    relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+
+    {
+        SCOPED_TRACE("after adding relative surface");
+        ScreenCapture::captureScreen(&sc);
+        // our relative surface should be visible now.
+        sc->checkPixel(75, 75, 255, 177, 177);
+    }
+
+    // A call to setLayer will override a call to setRelativeLayer
+    SurfaceComposerClient::openGlobalTransaction();
+    relativeSurfaceControl->setLayer(0);
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    {
+        SCOPED_TRACE("after set layer");
+        ScreenCapture::captureScreen(&sc);
+        // now the FG surface should be visible again.
+        sc->expectFGColor(75, 75);
+    }
+}
+
+class ChildLayerTest : public LayerUpdateTest {
+protected:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+        mChild = mComposerClient->createSurface(
+                String8("Child surface"),
+                10, 10, PIXEL_FORMAT_RGBA_8888,
+                0, mFGSurfaceControl.get());
+        fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        {
+            SCOPED_TRACE("before anything");
+            ScreenCapture::captureScreen(&mCapture);
+            mCapture->expectChildColor(64, 64);
+        }
+    }
+    void TearDown() override {
+        LayerUpdateTest::TearDown();
+        mChild = 0;
+    }
+
+    sp<SurfaceControl> mChild;
+    sp<ScreenCapture> mCapture;
+};
+
+TEST_F(ChildLayerTest, ChildLayerPositioning) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground should now be at 0, 0
+        mCapture->expectFGColor(0, 0);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(10, 10);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerCropping) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerFinalCropping) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerConstraints) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mFGSurfaceControl->setPosition(0, 0);
+    mChild->setPosition(63, 63);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(0, 0);
+        // Last pixel in foreground should now be the child.
+        mCapture->expectChildColor(63, 63);
+        // But the child should be constrained and the next pixel
+        // must be the background
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerScaling) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setPosition(0, 0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // Find the boundary between the parent and child
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // The boundary should be twice as far from the origin now.
+    // The pixels from the last test should all be child now
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 19);
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerAlpha) {
+    fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
+    fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
+    fillSurfaceRGBA8(mChild, 0, 254, 0);
+    waitForPostedBuffers();
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Unblended child color
+        mCapture->checkPixel(0, 0, 0, 254, 0);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 127, 127, 0);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 95, 64, 95);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+    mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->detachChildren();
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->hide();
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // Nothing should have changed.
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        // But it's only 10x10.
+        mCapture->expectFGColor(10, 10);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    // We cause scaling by 2.
+    mFGSurfaceControl->setSize(128, 128);
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 19);
+        // And now it should be scaled all the way to 20x20
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        // But it's only 10x10.
+        mCapture->expectFGColor(10, 10);
+    }
+
+
+    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+    // the WM specified state size.
+    mFGSurfaceControl->setSize(128, 64);
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    {
+        // The child should still be in the same place and not have any strange scaling as in
+        // b/37673612.
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectFGColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+    // Destroy the child layer
+    mChild.clear();
+
+    // Now recreate it as hidden
+    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
+                                            mFGSurfaceControl.get());
+
+    // Show the child layer in a deferred transaction
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(),
+                                  mFGSurfaceControl->getSurface()->getNextFrameNumber());
+    mChild->show();
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // Render the foreground surface a few times
+    //
+    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+    // never acquire/release the first buffer
+    ALOGI("Filling 1");
+    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+    ALOGI("Filling 2");
+    fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
+    ALOGI("Filling 3");
+    fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
+    ALOGI("Filling 4");
+    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+}
+
 }
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
new file mode 100644
index 0000000..203ced5
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := test-hwc2
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+    -DEGL_EGLEXT_PROTOTYPES \
+    -DGL_GLEXT_PROTOTYPES
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libhardware \
+    libEGL \
+    libGLESv2 \
+    libui \
+    libgui \
+    liblog \
+    libsync
+LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    libadf \
+    libadfhwc \
+    libmath
+LOCAL_SRC_FILES := \
+    Hwc2Test.cpp \
+    Hwc2TestProperties.cpp \
+    Hwc2TestLayer.cpp \
+    Hwc2TestLayers.cpp \
+    Hwc2TestBuffer.cpp \
+    Hwc2TestClientTarget.cpp \
+    Hwc2TestVirtualDisplay.cpp
+
+include $(BUILD_NATIVE_TEST)
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
new file mode 100644
index 0000000..062485e
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -0,0 +1,4556 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <unordered_set>
+#include <unordered_map>
+#include <gtest/gtest.h>
+#include <dlfcn.h>
+#include <android-base/unique_fd.h>
+#include <hardware/hardware.h>
+#include <sync/sync.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestLayer.h"
+#include "Hwc2TestLayers.h"
+#include "Hwc2TestClientTarget.h"
+#include "Hwc2TestVirtualDisplay.h"
+
+void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int32_t connected);
+void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int64_t timestamp);
+
+class Hwc2Test : public testing::Test {
+public:
+
+    virtual void SetUp()
+    {
+        hw_module_t const* hwc2Module;
+
+        int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwc2Module);
+        ASSERT_GE(err, 0) << "failed to get hwc hardware module: "
+                << strerror(-err);
+
+        /* The following method will fail if you have not run
+         * "adb shell stop" */
+        err = hwc2_open(hwc2Module, &mHwc2Device);
+        ASSERT_GE(err, 0) << "failed to open hwc hardware module: "
+                << strerror(-err);
+
+        populateDisplays();
+    }
+
+    virtual void TearDown()
+    {
+
+        for (auto itr = mLayers.begin(); itr != mLayers.end();) {
+            hwc2_display_t display = itr->first;
+            hwc2_layer_t layer = itr->second;
+            itr++;
+            /* Destroys and removes the layer from mLayers */
+            destroyLayer(display, layer);
+        }
+
+        for (auto itr = mActiveDisplays.begin(); itr != mActiveDisplays.end();) {
+            hwc2_display_t display = *itr;
+            itr++;
+            /* Sets power mode to off and removes the display from
+             * mActiveDisplays */
+            setPowerMode(display, HWC2_POWER_MODE_OFF);
+        }
+
+        for (auto itr = mVirtualDisplays.begin(); itr != mVirtualDisplays.end();) {
+            hwc2_display_t display = *itr;
+            itr++;
+            /* Destroys virtual displays */
+            destroyVirtualDisplay(display);
+        }
+
+        if (mHwc2Device)
+            hwc2_close(mHwc2Device);
+    }
+
+    void registerCallback(hwc2_callback_descriptor_t descriptor,
+            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_REGISTER_CALLBACK>(
+                getFunction(HWC2_FUNCTION_REGISTER_CALLBACK));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, descriptor,
+                callbackData, pointer));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to register callback";
+        }
+    }
+
+    void getDisplayType(hwc2_display_t display, hwc2_display_type_t* outType,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_TYPE>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_TYPE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    reinterpret_cast<int32_t*>(outType)));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display type";
+        }
+    }
+
+    /* If the populateDisplays function is still receiving displays and the
+     * display is connected, the display handle is stored in mDisplays. */
+    void hotplugCallback(hwc2_display_t display, int32_t connected)
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+
+        if (mHotplugStatus != Hwc2TestHotplugStatus::Receiving)
+            return;
+
+        if (connected == HWC2_CONNECTION_CONNECTED)
+            mDisplays.insert(display);
+
+        mHotplugCv.notify_all();
+    }
+
+    void createLayer(hwc2_display_t display, hwc2_layer_t* outLayer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_LAYER>(
+                getFunction(HWC2_FUNCTION_CREATE_LAYER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outLayer));
+
+        if (err == HWC2_ERROR_NONE)
+            mLayers.insert(std::make_pair(display, *outLayer));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
+        }
+    }
+
+    void destroyLayer(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_LAYER>(
+                getFunction(HWC2_FUNCTION_DESTROY_LAYER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer));
+
+        if (err == HWC2_ERROR_NONE)
+            mLayers.erase(std::make_pair(display, layer));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy layer "
+                    << layer;
+        }
+    }
+
+    void getDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
+            hwc2_attribute_t attribute, int32_t* outValue,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config,
+                attribute, outValue));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display attribute "
+                    << getAttributeName(attribute) << " for config " << config;
+        }
+    }
+
+    void getDisplayConfigs(hwc2_display_t display,
+            std::vector<hwc2_config_t>* outConfigs,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_CONFIGS));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numConfigs = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numConfigs, nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            outConfigs->resize(numConfigs);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numConfigs, outConfigs->data()));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get configs for"
+                    " display " << display;
+        }
+    }
+
+    void getActiveConfig(hwc2_display_t display, hwc2_config_t* outConfig,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_ACTIVE_CONFIG>(
+                getFunction(HWC2_FUNCTION_GET_ACTIVE_CONFIG));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outConfig));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get active config on"
+                    " display " << display;
+        }
+    }
+
+    void setActiveConfig(hwc2_display_t display, hwc2_config_t config,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_ACTIVE_CONFIG>(
+                getFunction(HWC2_FUNCTION_SET_ACTIVE_CONFIG));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set active config "
+                    << config;
+        }
+    }
+
+    void getDozeSupport(hwc2_display_t display, int32_t* outSupport,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DOZE_SUPPORT>(
+                getFunction(HWC2_FUNCTION_GET_DOZE_SUPPORT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outSupport));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get doze support on"
+                    " display " << display;
+        }
+    }
+
+    void setPowerMode(hwc2_display_t display, hwc2_power_mode_t mode,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_POWER_MODE>(
+                getFunction(HWC2_FUNCTION_SET_POWER_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                mode));
+        if (outErr) {
+            *outErr = err;
+            if (err != HWC2_ERROR_NONE)
+                return;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set power mode "
+                    << getPowerModeName(mode) << " on display " << display;
+        }
+
+        if (mode == HWC2_POWER_MODE_OFF) {
+            mActiveDisplays.erase(display);
+        } else {
+            mActiveDisplays.insert(display);
+        }
+    }
+
+    void setVsyncEnabled(hwc2_display_t display, hwc2_vsync_t enabled,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_VSYNC_ENABLED>(
+                getFunction(HWC2_FUNCTION_SET_VSYNC_ENABLED));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                enabled));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set vsync enabled "
+                    << getVsyncName(enabled);
+        }
+    }
+
+    void vsyncCallback(hwc2_display_t display, int64_t timestamp)
+    {
+        std::lock_guard<std::mutex> lock(mVsyncMutex);
+        mVsyncDisplay = display;
+        mVsyncTimestamp = timestamp;
+        mVsyncCv.notify_all();
+    }
+
+    void getDisplayName(hwc2_display_t display, std::string* outName,
+                hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_NAME>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_NAME));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t size = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
+                nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            std::vector<char> name(size);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
+                    name.data()));
+
+            outName->assign(name.data());
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display name for "
+                    << display;
+        }
+    }
+
+    void setLayerCompositionType(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_composition_t composition, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                composition));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer composition"
+                    " type " << getCompositionName(composition);
+        }
+    }
+
+    void setCursorPosition(hwc2_display_t display, hwc2_layer_t layer,
+            int32_t x, int32_t y, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_CURSOR_POSITION>(
+                getFunction(HWC2_FUNCTION_SET_CURSOR_POSITION));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer, x,
+                y));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position";
+        }
+    }
+
+    void setLayerBlendMode(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_blend_mode_t mode, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_BLEND_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                mode));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer blend mode "
+                    << getBlendModeName(mode);
+        }
+    }
+
+    void setLayerBuffer(hwc2_display_t display, hwc2_layer_t layer,
+            buffer_handle_t buffer, int32_t acquireFence,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BUFFER>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_BUFFER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                buffer, acquireFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer buffer";
+        }
+    }
+
+    void setLayerColor(hwc2_display_t display, hwc2_layer_t layer,
+            hwc_color_t color, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COLOR>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_COLOR));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                color));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer color";
+        }
+    }
+
+    void setLayerDataspace(hwc2_display_t display, hwc2_layer_t layer,
+            android_dataspace_t dataspace, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DATASPACE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_DATASPACE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                layer, dataspace));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer dataspace";
+        }
+    }
+
+    void setLayerDisplayFrame(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_rect_t& displayFrame, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                displayFrame));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer display"
+                    " frame";
+        }
+    }
+
+    void setLayerPlaneAlpha(hwc2_display_t display, hwc2_layer_t layer,
+            float alpha, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                alpha));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer plane alpha "
+                    << alpha;
+        }
+    }
+
+    void setLayerSourceCrop(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_frect_t& sourceCrop, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_SOURCE_CROP));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                sourceCrop));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer source crop";
+        }
+    }
+
+    void setLayerSurfaceDamage(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_region_t& surfaceDamage, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                surfaceDamage));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer surface"
+                    " damage";
+        }
+    }
+
+    void setLayerTransform(hwc2_display_t display, hwc2_layer_t layer,
+            hwc_transform_t transform, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_TRANSFORM>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_TRANSFORM));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                transform));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer transform "
+                    << getTransformName(transform);
+        }
+    }
+
+    void setLayerVisibleRegion(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_region_t& visibleRegion, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                visibleRegion));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer visible"
+                    " region";
+        }
+    }
+
+    void setLayerZOrder(hwc2_display_t display, hwc2_layer_t layer,
+            uint32_t zOrder, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_Z_ORDER>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_Z_ORDER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                zOrder));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer z order "
+                    << zOrder;
+        }
+    }
+
+    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
+            uint32_t* outNumRequests, hwc2_error_t* outErr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_VALIDATE_DISPLAY>(
+                getFunction(HWC2_FUNCTION_VALIDATE_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        *outErr = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outNumTypes, outNumRequests));
+    }
+
+    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
+            uint32_t* outNumRequests, bool* outHasChanges)
+    {
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        EXPECT_NO_FATAL_FAILURE(validateDisplay(display, outNumTypes,
+                outNumRequests, &err));
+
+        if (err != HWC2_ERROR_HAS_CHANGES) {
+            *outHasChanges = false;
+            EXPECT_EQ(err, HWC2_ERROR_NONE) << "failed to validate display";
+        } else {
+            *outHasChanges = true;
+        }
+    }
+
+    void getDisplayRequests(hwc2_display_t display,
+            hwc2_display_request_t* outDisplayRequests,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<hwc2_layer_request_t>* outLayerRequests,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_REQUESTS));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
+                nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE && numElements > 0) {
+            outLayers->resize(numElements);
+            outLayerRequests->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
+                    reinterpret_cast<uint64_t*>(outLayers->data()),
+                    reinterpret_cast<int32_t*>(outLayerRequests->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display requests";
+        }
+    }
+
+    void handleRequests(hwc2_display_t display,
+            const std::vector<hwc2_layer_t>& layers, uint32_t numRequests,
+            std::set<hwc2_layer_t>* outClearLayers = nullptr,
+            bool* outFlipClientTarget = nullptr)
+    {
+        hwc2_display_request_t displayRequest =
+                static_cast<hwc2_display_request_t>(0);
+        std::vector<hwc2_layer_t> requestedLayers;
+        std::vector<hwc2_layer_request_t> requests;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequest,
+                &requestedLayers, &requests));
+
+        EXPECT_EQ(numRequests, requests.size()) << "validate returned "
+                << numRequests << " requests and get display requests returned "
+                << requests.size() << " requests";
+
+        for (size_t i = 0; i < requests.size(); i++) {
+            hwc2_layer_t requestedLayer = requestedLayers.at(i);
+            hwc2_layer_request_t request = requests.at(i);
+
+            EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
+                    0) << "get display requests returned an unknown layer";
+            EXPECT_NE(request, 0) << "returned empty request for layer "
+                    << requestedLayer;
+
+            if (outClearLayers && request
+                    == HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET)
+                outClearLayers->insert(requestedLayer);
+        }
+
+        if (outFlipClientTarget)
+            *outFlipClientTarget = displayRequest
+                    & HWC2_DISPLAY_REQUEST_FLIP_CLIENT_TARGET;
+    }
+
+    void getChangedCompositionTypes(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<hwc2_composition_t>* outTypes,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+                getFunction(HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numElements, nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE && numElements > 0) {
+            outLayers->resize(numElements);
+            outTypes->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numElements, reinterpret_cast<uint64_t*>(outLayers->data()),
+                    reinterpret_cast<int32_t*>(outTypes->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get changed"
+                    " composition types";
+        }
+    }
+
+    void handleCompositionChanges(hwc2_display_t display,
+            const Hwc2TestLayers& testLayers,
+            const std::vector<hwc2_layer_t>& layers, uint32_t numTypes,
+            std::set<hwc2_layer_t>* outClientLayers = nullptr)
+    {
+        std::vector<hwc2_layer_t> changedLayers;
+        std::vector<hwc2_composition_t> types;
+
+        ASSERT_NO_FATAL_FAILURE(getChangedCompositionTypes(display,
+                &changedLayers, &types));
+
+        EXPECT_EQ(numTypes, types.size()) << "validate returned "
+                << numTypes << " types and get changed composition types"
+                " returned " << types.size() << " types";
+
+        for (size_t i = 0; i < types.size(); i++) {
+
+            auto layer = std::find(layers.begin(), layers.end(),
+                    changedLayers.at(i));
+
+            EXPECT_TRUE(layer != layers.end() || !testLayers.contains(*layer))
+                    << "get changed composition types returned an unknown layer";
+
+            hwc2_composition_t requestedType = testLayers.getComposition(*layer);
+            hwc2_composition_t returnedType = types.at(i);
+
+            EXPECT_NE(returnedType, HWC2_COMPOSITION_INVALID) << "get changed"
+                    " composition types returned invalid composition";
+
+            switch (requestedType) {
+            case HWC2_COMPOSITION_CLIENT:
+                EXPECT_TRUE(false) << getCompositionName(returnedType)
+                        << " cannot be changed";
+                break;
+            case HWC2_COMPOSITION_DEVICE:
+            case HWC2_COMPOSITION_SOLID_COLOR:
+                EXPECT_EQ(returnedType, HWC2_COMPOSITION_CLIENT)
+                        << "composition of type "
+                        << getCompositionName(requestedType)
+                        << " can only be changed to "
+                        << getCompositionName(HWC2_COMPOSITION_CLIENT);
+                break;
+            case HWC2_COMPOSITION_CURSOR:
+            case HWC2_COMPOSITION_SIDEBAND:
+                EXPECT_TRUE(returnedType == HWC2_COMPOSITION_CLIENT
+                        || returnedType == HWC2_COMPOSITION_DEVICE)
+                        << "composition of type "
+                        << getCompositionName(requestedType)
+                        << " can only be changed to "
+                        << getCompositionName(HWC2_COMPOSITION_CLIENT) << " or "
+                        << getCompositionName(HWC2_COMPOSITION_DEVICE);
+                break;
+            default:
+                EXPECT_TRUE(false) << "unknown type "
+                        << getCompositionName(requestedType);
+                break;
+            }
+
+            if (outClientLayers)
+                if (returnedType == HWC2_COMPOSITION_CLIENT)
+                    outClientLayers->insert(*layer);
+        }
+
+        if (outClientLayers) {
+            for (auto layer : layers) {
+                if (testLayers.getComposition(layer) == HWC2_COMPOSITION_CLIENT)
+                    outClientLayers->insert(layer);
+            }
+        }
+    }
+
+    void acceptDisplayChanges(hwc2_display_t display,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+                getFunction(HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to accept display changes";
+        }
+    }
+
+    void getClientTargetSupport(hwc2_display_t display, int32_t width,
+            int32_t height, android_pixel_format_t format,
+            android_dataspace_t dataspace, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
+                getFunction(HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, width,
+                height, format, dataspace));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get client target"
+                    " support";
+        }
+    }
+
+    void setClientTarget(hwc2_display_t display, buffer_handle_t handle,
+            int32_t acquireFence, android_dataspace_t dataspace,
+            hwc_region_t damage, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_CLIENT_TARGET>(
+                getFunction(HWC2_FUNCTION_SET_CLIENT_TARGET));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, handle,
+                acquireFence, dataspace, damage));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set client target";
+        }
+    }
+
+    void presentDisplay(hwc2_display_t display, int32_t* outPresentFence,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_PRESENT_DISPLAY>(
+                getFunction(HWC2_FUNCTION_PRESENT_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outPresentFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to present display";
+        }
+    }
+
+    void getReleaseFences(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<int32_t>* outFences, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_RELEASE_FENCES>(
+                getFunction(HWC2_FUNCTION_GET_RELEASE_FENCES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numElements, nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            outLayers->resize(numElements);
+            outFences->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numElements, outLayers->data(), outFences->data()));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get release fences";
+        }
+    }
+
+    void getColorModes(hwc2_display_t display,
+            std::vector<android_color_mode_t>* outColorModes,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_COLOR_MODES>(
+                getFunction(HWC2_FUNCTION_GET_COLOR_MODES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numColorModes = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numColorModes, nullptr));
+        if (err == HWC2_ERROR_NONE) {
+            outColorModes->resize(numColorModes);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numColorModes,
+                    reinterpret_cast<int32_t*>(outColorModes->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get color modes for"
+                    " display " << display;
+        }
+    }
+
+    void setColorMode(hwc2_display_t display, android_color_mode_t colorMode,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_MODE>(
+                getFunction(HWC2_FUNCTION_SET_COLOR_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                static_cast<int32_t>(colorMode)));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color mode "
+                    << colorMode;
+        }
+    }
+
+    void getHdrCapabilities(hwc2_display_t display,
+            std::vector<android_hdr_t>* outTypes, float* outMaxLuminance,
+            float* outMaxAverageLuminance, float* outMinLuminance,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_HDR_CAPABILITIES>(
+                getFunction(HWC2_FUNCTION_GET_HDR_CAPABILITIES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numTypes = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numTypes, nullptr, outMaxLuminance, outMaxAverageLuminance,
+                outMinLuminance));
+
+        if (err == HWC2_ERROR_NONE) {
+            outTypes->resize(numTypes);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &numTypes,
+                    reinterpret_cast<int32_t*>(outTypes->data()), outMaxLuminance,
+                    outMaxAverageLuminance, outMinLuminance));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get hdr capabilities"
+                    " for display " << display;
+        }
+    }
+
+    void setColorTransform(hwc2_display_t display,
+            const std::array<float, 16>& matrix, android_color_transform_t hint,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_TRANSFORM>(
+                getFunction(HWC2_FUNCTION_SET_COLOR_TRANSFORM));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                matrix.data(), hint));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color transform "
+                    << hint;
+        }
+    }
+
+    void createVirtualDisplay(uint32_t width, uint32_t height,
+            android_pixel_format_t* outFormat, hwc2_display_t* outDisplay,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+                getFunction(HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, width, height,
+                reinterpret_cast<int32_t*>(outFormat), outDisplay));
+
+        if (err == HWC2_ERROR_NONE)
+            mVirtualDisplays.insert(*outDisplay);
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create virtual display";
+        }
+    }
+
+    void destroyVirtualDisplay(hwc2_display_t display,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+                getFunction(HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
+
+        if (err == HWC2_ERROR_NONE)
+            mVirtualDisplays.erase(display);
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy virtual display";
+        }
+    }
+
+    void getMaxVirtualDisplayCount(uint32_t* outMaxCnt)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+                getFunction(HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        *outMaxCnt = pfn(mHwc2Device);
+    }
+
+    void setOutputBuffer(hwc2_display_t display, buffer_handle_t buffer,
+            int32_t releaseFence, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_OUTPUT_BUFFER>(
+                getFunction(HWC2_FUNCTION_SET_OUTPUT_BUFFER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, buffer,
+                releaseFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set output buffer";
+        }
+    }
+
+    void dump(std::string* outBuffer)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DUMP>(
+                getFunction(HWC2_FUNCTION_DUMP));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t size = 0;
+
+        pfn(mHwc2Device, &size, nullptr);
+
+        std::vector<char> buffer(size);
+
+        pfn(mHwc2Device, &size, buffer.data());
+
+        outBuffer->assign(buffer.data());
+    }
+
+    void getBadDisplay(hwc2_display_t* outDisplay)
+    {
+        for (hwc2_display_t display = 0; display < UINT64_MAX; display++) {
+            if (mDisplays.count(display) == 0) {
+                *outDisplay = display;
+                return;
+            }
+        }
+        ASSERT_TRUE(false) << "Unable to find bad display. UINT64_MAX displays"
+                " are registered. This should never happen.";
+    }
+
+    void waitForVsync(hwc2_display_t* outDisplay = nullptr,
+            int64_t* outTimestamp = nullptr)
+    {
+        std::unique_lock<std::mutex> lock(mVsyncMutex);
+        ASSERT_EQ(mVsyncCv.wait_for(lock, std::chrono::seconds(3)),
+                std::cv_status::no_timeout) << "timed out attempting to get"
+                " vsync callback";
+        if (outDisplay)
+            *outDisplay = mVsyncDisplay;
+        if (outTimestamp)
+            *outTimestamp = mVsyncTimestamp;
+    }
+
+    void enableVsync(hwc2_display_t display)
+    {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, this,
+                reinterpret_cast<hwc2_function_pointer_t>(
+                hwc2TestVsyncCallback)));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+    }
+
+    void disableVsync(hwc2_display_t display)
+    {
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+    }
+
+protected:
+    hwc2_function_pointer_t getFunction(hwc2_function_descriptor_t descriptor)
+    {
+        return mHwc2Device->getFunction(mHwc2Device, descriptor);
+    }
+
+    void getCapabilities(std::vector<hwc2_capability_t>* outCapabilities)
+    {
+        uint32_t num = 0;
+
+        mHwc2Device->getCapabilities(mHwc2Device, &num, nullptr);
+
+        outCapabilities->resize(num);
+
+        mHwc2Device->getCapabilities(mHwc2Device, &num,
+                reinterpret_cast<int32_t*>(outCapabilities->data()));
+    }
+
+    /* Registers a hotplug callback and waits for hotplug callbacks. This
+     * function will have no effect if called more than once. */
+    void populateDisplays()
+    {
+        /* Sets the hotplug status to receiving */
+        {
+            std::lock_guard<std::mutex> lock(mHotplugMutex);
+
+            if (mHotplugStatus != Hwc2TestHotplugStatus::Init)
+                return;
+            mHotplugStatus = Hwc2TestHotplugStatus::Receiving;
+        }
+
+        /* Registers the callback. This function call cannot be locked because
+         * a callback could happen on the same thread */
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_HOTPLUG, this,
+                reinterpret_cast<hwc2_function_pointer_t>(
+                hwc2TestHotplugCallback)));
+
+        /* Waits for hotplug events. If a hotplug event has not come within 1
+         * second, stop waiting. */
+        std::unique_lock<std::mutex> lock(mHotplugMutex);
+
+        while (mHotplugCv.wait_for(lock, std::chrono::seconds(1)) !=
+                std::cv_status::timeout) { }
+
+        /* Sets the hotplug status to done. Future calls will have no effect */
+        mHotplugStatus = Hwc2TestHotplugStatus::Done;
+    }
+
+    /* NOTE: will create min(newlayerCnt, max supported layers) layers */
+    void createLayers(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers, size_t newLayerCnt)
+    {
+        std::vector<hwc2_layer_t> newLayers;
+        hwc2_layer_t layer;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        for (size_t i = 0; i < newLayerCnt; i++) {
+
+            EXPECT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
+            if (err == HWC2_ERROR_NO_RESOURCES)
+                break;
+            if (err != HWC2_ERROR_NONE) {
+                newLayers.clear();
+                ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
+            }
+            newLayers.push_back(layer);
+        }
+
+        *outLayers = std::move(newLayers);
+    }
+
+    void destroyLayers(hwc2_display_t display,
+            std::vector<hwc2_layer_t>&& layers)
+    {
+        for (hwc2_layer_t layer : layers) {
+            EXPECT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+        }
+    }
+
+    void getInvalidConfig(hwc2_display_t display, hwc2_config_t* outConfig)
+    {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        hwc2_config_t CONFIG_MAX = UINT32_MAX;
+
+        ASSERT_LE(configs.size() - 1, CONFIG_MAX) << "every config value"
+                " (2^32 values) has been taken which shouldn't happen";
+
+        hwc2_config_t config;
+        for (config = 0; config < CONFIG_MAX; config++) {
+            if (std::count(configs.begin(), configs.end(), config) == 0)
+                break;
+        }
+
+        *outConfig = config;
+    }
+
+    /* Calls a set property function from Hwc2Test to set a property value from
+     * Hwc2TestLayer to hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
+
+    /* Calls a set property function from Hwc2Test to set property values from
+     * Hwc2TestLayers to hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertiesFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayers* testLayers);
+
+    /* Calls a set property function from Hwc2Test to set a bad property value
+     * on hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyBadLayerFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
+
+    /* Calls a set property function from Hwc2Test to set a bad property value
+     * on hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyBadParameterFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer, hwc2_error_t* outErr);
+
+    /* Is called after a display is powered on and all layer properties have
+     * been set. It should be used to test functions such as validate, accepting
+     * changes, present, etc. */
+    using TestDisplayLayersFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestLayers* testLayers);
+
+    /* It is called on an non validated display */
+    using TestDisplayNonValidatedLayersFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, std::vector<hwc2_layer_t>* layers);
+
+    /* Tests client target support on a particular display and config */
+    using TestClientTargetSupportFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display,
+            const Hwc2TestClientTargetSupport& testClientTargetSupport);
+
+    /* Tests a particular active display config */
+    using TestActiveDisplayConfigFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display);
+
+    /* Tests a newly created virtual display */
+    using TestCreateVirtualDisplayFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, Hwc2TestVirtualDisplay* testVirtualDisplay);
+
+    /* Advances a property of Hwc2TestLayer */
+    using AdvanceProperty = bool (*)(Hwc2TestLayer* testLayer);
+
+    /* Advances properties of Hwc2TestLayers */
+    using AdvanceProperties = bool (*)(Hwc2TestLayers* testLayer);
+
+    /* Advances properties of Hwc2TestClientTargetSupport */
+    using AdvanceClientTargetSupport = bool (*)(
+            Hwc2TestClientTargetSupport* testClientTargetSupport);
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, sets the property and then
+     * destroys the layer */
+    void setLayerProperty(Hwc2TestCoverage coverage,
+            TestLayerPropertyFunction function, AdvanceProperty advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                do {
+                    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                            &testLayer, nullptr));
+
+                    ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+                } while (advance(&testLayer));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, cycles through each property
+     * value and updates the layer property value and then destroys the layer */
+    void setLayerPropertyUpdate(Hwc2TestCoverage coverage,
+            TestLayerPropertyFunction function, AdvanceProperty advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                do {
+                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                            &testLayer, nullptr));
+                } while (advance(&testLayer));
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates multiple layers, calls the
+     * TestLayerPropertiesFunction to set property values and then
+     * destroys the layers */
+    void setLayerProperties(Hwc2TestCoverage coverage, size_t layerCnt,
+            TestLayerPropertiesFunction function, AdvanceProperties advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                std::vector<hwc2_layer_t> layers;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea);
+
+                do {
+                    for (auto layer : layers) {
+                        EXPECT_NO_FATAL_FAILURE(function(this, display, layer,
+                                &testLayers));
+                    }
+                } while (advance(&testLayers));
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config.
+     * 1) It attempts to set a valid property value to bad layer handle.
+     * 2) It creates a layer x and attempts to set a valid property value to
+     *    layer x + 1
+     * 3) It destroys the layer x and attempts to set a valid property value to
+     *    the destroyed layer x.
+     */
+    void setLayerPropertyBadLayer(Hwc2TestCoverage coverage,
+            TestLayerPropertyBadLayerFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer = 0;
+                Area displayArea;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer + 1,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, sets a bad property value and
+     * then destroys the layer */
+    void setLayerPropertyBadParameter(TestLayerPropertyBadParameterFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong"
+                        " error code";
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+            }
+        }
+    }
+
+    /* For each active display it powers on the display, cycles through each
+     * config and creates a set of layers with a certain amount of coverage.
+     * For each active display, for each config and for each set of layers,
+     * it calls the TestDisplayLayersFunction */
+    void displayLayers(Hwc2TestCoverage coverage, size_t layerCnt,
+            TestDisplayLayersFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+                std::vector<hwc2_layer_t> layers;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea);
+
+                do {
+                    bool skip;
+
+                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                            &testLayers, &skip));
+                    if (!skip)
+                        EXPECT_NO_FATAL_FAILURE(function(this, display, layers,
+                                &testLayers));
+
+                } while (testLayers.advance());
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
+                        std::move(layers)));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    /* For each active display, it calls the
+     * TestDisplayNonValidatedLayersFunction on a variety on non-validated
+     * layer combinations */
+    void displayNonValidatedLayers(size_t layerCnt,
+            TestDisplayNonValidatedLayersFunction function)
+    {
+        for (auto display : mDisplays) {
+            uint32_t numTypes, numRequests;
+            std::vector<hwc2_layer_t> layers;
+            bool hasChanges;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            for (auto layer : layers) {
+                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                        HWC2_COMPOSITION_CLIENT));
+            }
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                    &numRequests, &hasChanges));
+
+            for (auto layer : layers) {
+                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                        HWC2_COMPOSITION_DEVICE));
+            }
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    /* Test client target support on each config on each active display */
+    void setClientTargetSupport(Hwc2TestCoverage coverage,
+            TestClientTargetSupportFunction function,
+            AdvanceClientTargetSupport advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestClientTargetSupport testClientTargetSupport(coverage,
+                        displayArea);
+
+                do {
+                    EXPECT_NO_FATAL_FAILURE(function(this, display,
+                            testClientTargetSupport));
+
+                } while (advance(&testClientTargetSupport));
+            }
+        }
+    }
+
+    /* Cycles through each config on each active display and calls
+     * a TestActiveDisplayConfigFunction */
+    void setActiveDisplayConfig(TestActiveDisplayConfigFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+                EXPECT_NO_FATAL_FAILURE(function(this, display));
+            }
+        }
+    }
+
+    /* Creates a virtual display for testing */
+    void createVirtualDisplay(Hwc2TestCoverage coverage,
+            TestCreateVirtualDisplayFunction function)
+    {
+        Hwc2TestVirtualDisplay testVirtualDisplay(coverage);
+
+        do {
+            hwc2_display_t display;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            const UnsignedArea& dimension =
+                    testVirtualDisplay.getDisplayDimension();
+            android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+
+            ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
+                    dimension.height, &desiredFormat, &display, &err));
+
+            EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
+                    || err == HWC2_ERROR_UNSUPPORTED)
+                    << "returned wrong error code";
+            EXPECT_GE(desiredFormat, 0) << "invalid format";
+
+            if (err != HWC2_ERROR_NONE)
+                continue;
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display,
+                    &testVirtualDisplay));
+
+            ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+
+        } while (testVirtualDisplay.advance());
+    }
+
+
+    void getActiveConfigAttribute(hwc2_display_t display,
+            hwc2_attribute_t attribute, int32_t* outValue)
+    {
+        hwc2_config_t config;
+        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &config));
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                attribute, outValue));
+        ASSERT_GE(*outValue, 0) << "failed to get valid "
+                << getAttributeName(attribute);
+    }
+
+    void getActiveDisplayArea(hwc2_display_t display, Area* displayArea)
+    {
+        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
+                HWC2_ATTRIBUTE_WIDTH, &displayArea->width));
+        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
+                HWC2_ATTRIBUTE_HEIGHT, &displayArea->height));
+    }
+
+    void closeFences(hwc2_display_t display, int32_t presentFence)
+    {
+        std::vector<hwc2_layer_t> layers;
+        std::vector<int32_t> fences;
+        const int msWait = 3000;
+
+        if (presentFence >= 0) {
+            ASSERT_GE(sync_wait(presentFence, msWait), 0);
+            close(presentFence);
+        }
+
+        ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences));
+        EXPECT_EQ(layers.size(), fences.size());
+
+        for (int32_t fence : fences) {
+            EXPECT_GE(sync_wait(fence, msWait), 0);
+            if (fence >= 0)
+                close(fence);
+        }
+    }
+
+    void setLayerProperties(hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayers* testLayers, bool* outSkip)
+    {
+        hwc2_composition_t composition;
+        buffer_handle_t handle = nullptr;
+        int32_t acquireFence;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+        *outSkip = true;
+
+        if (!testLayers->contains(layer))
+            return;
+
+        composition = testLayers->getComposition(layer);
+
+        /* If the device cannot support a buffer format, then do not continue */
+        if ((composition == HWC2_COMPOSITION_DEVICE
+                || composition == HWC2_COMPOSITION_CURSOR)
+                && testLayers->getBuffer(layer, &handle, &acquireFence) < 0)
+            return;
+
+        EXPECT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                composition, &err));
+        if (err == HWC2_ERROR_UNSUPPORTED)
+            EXPECT_TRUE(composition != HWC2_COMPOSITION_CLIENT
+                    && composition != HWC2_COMPOSITION_DEVICE);
+
+        const hwc_rect_t cursor = testLayers->getCursorPosition(layer);
+
+        EXPECT_NO_FATAL_FAILURE(setLayerBuffer(display, layer, handle,
+                acquireFence));
+        EXPECT_NO_FATAL_FAILURE(setLayerBlendMode(display, layer,
+                testLayers->getBlendMode(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
+                testLayers->getColor(layer)));
+        EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left,
+                cursor.top));
+        EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
+                testLayers->getDataspace(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
+                testLayers->getDisplayFrame(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerPlaneAlpha(display, layer,
+                testLayers->getPlaneAlpha(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerSourceCrop(display, layer,
+                testLayers->getSourceCrop(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerSurfaceDamage(display, layer,
+                testLayers->getSurfaceDamage(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerTransform(display, layer,
+                testLayers->getTransform(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerVisibleRegion(display, layer,
+                testLayers->getVisibleRegion(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer,
+                testLayers->getZOrder(layer)));
+
+        *outSkip = false;
+    }
+
+    void setLayerProperties(hwc2_display_t display,
+            const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestLayers* testLayers, bool* outSkip)
+    {
+        for (auto layer : layers) {
+            EXPECT_NO_FATAL_FAILURE(setLayerProperties(display, layer,
+                    testLayers, outSkip));
+            if (*outSkip)
+                return;
+        }
+    }
+
+    void setClientTarget(hwc2_display_t display,
+            Hwc2TestClientTarget* testClientTarget,
+            const Hwc2TestLayers& testLayers,
+            const std::set<hwc2_layer_t>& clientLayers,
+            const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
+            const Area& displayArea)
+    {
+        android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+        hwc_region_t damage = { };
+        buffer_handle_t handle;
+        int32_t acquireFence;
+
+        ASSERT_EQ(testClientTarget->getBuffer(testLayers, clientLayers,
+                clearLayers, flipClientTarget, displayArea, &handle,
+                &acquireFence), 0);
+        EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
+                dataspace, damage));
+    }
+
+    void presentDisplays(size_t layerCnt, Hwc2TestCoverage coverage,
+            const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>&
+            coverageExceptions, bool optimize)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+            ASSERT_NO_FATAL_FAILURE(enableVsync(display));
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+                std::vector<hwc2_layer_t> layers;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea,
+                        coverageExceptions);
+
+                if (optimize && !testLayers.optimizeLayouts())
+                    continue;
+
+                std::set<hwc2_layer_t> clientLayers;
+                std::set<hwc2_layer_t> clearLayers;
+                Hwc2TestClientTarget testClientTarget;
+
+                do {
+                    uint32_t numTypes, numRequests;
+                    bool hasChanges, skip;
+                    bool flipClientTarget;
+                    int32_t presentFence;
+
+                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                            &testLayers, &skip));
+                    if (skip)
+                        continue;
+
+                    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                            &numRequests, &hasChanges));
+                    if (hasChanges)
+                        EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                                << "wrong number of requests";
+
+                    ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
+                            testLayers, layers, numTypes, &clientLayers));
+                    ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
+                            numRequests, &clearLayers, &flipClientTarget));
+                    ASSERT_NO_FATAL_FAILURE(setClientTarget(display,
+                            &testClientTarget, testLayers, clientLayers,
+                            clearLayers, flipClientTarget, displayArea));
+                    ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display));
+
+                    ASSERT_NO_FATAL_FAILURE(waitForVsync());
+
+                    EXPECT_NO_FATAL_FAILURE(presentDisplay(display,
+                            &presentFence));
+
+                    ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence));
+
+                } while (testLayers.advance());
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
+                        std::move(layers)));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(disableVsync(display));
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    hwc2_device_t* mHwc2Device = nullptr;
+
+    enum class Hwc2TestHotplugStatus {
+        Init = 1,
+        Receiving,
+        Done,
+    };
+
+    std::mutex mHotplugMutex;
+    std::condition_variable mHotplugCv;
+    Hwc2TestHotplugStatus mHotplugStatus = Hwc2TestHotplugStatus::Init;
+    std::unordered_set<hwc2_display_t> mDisplays;
+
+    /* Store all created layers that have not been destroyed. If an ASSERT_*
+     * fails, then destroy the layers on exit */
+    std::set<std::pair<hwc2_display_t, hwc2_layer_t>> mLayers;
+
+    /* Store the power mode state. If it is not HWC2_POWER_MODE_OFF when
+     * tearing down the test cases, change it to HWC2_POWER_MODE_OFF */
+    std::set<hwc2_display_t> mActiveDisplays;
+
+    /* Store all created virtual displays that have not been destroyed. If an
+     * ASSERT_* fails, then destroy the virtual displays on exit */
+    std::set<hwc2_display_t> mVirtualDisplays;
+
+    std::mutex mVsyncMutex;
+    std::condition_variable mVsyncCv;
+    hwc2_display_t mVsyncDisplay;
+    int64_t mVsyncTimestamp = -1;
+};
+
+void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int32_t connection)
+{
+    if (callbackData)
+        static_cast<Hwc2Test*>(callbackData)->hotplugCallback(display,
+                connection);
+}
+
+void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int64_t timestamp)
+{
+    if (callbackData)
+        static_cast<Hwc2Test*>(callbackData)->vsyncCallback(display,
+                timestamp);
+}
+
+void setBlendMode(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
+            testLayer->getBlendMode(), outErr));
+}
+
+void setBuffer(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    buffer_handle_t handle;
+    android::base::unique_fd acquireFence;
+    hwc2_composition_t composition = testLayer->getComposition();
+
+    if (composition == HWC2_COMPOSITION_CLIENT
+            || composition == HWC2_COMPOSITION_SOLID_COLOR
+            || composition == HWC2_COMPOSITION_SIDEBAND)
+        return;
+
+    if (testLayer->getBuffer(&handle, &acquireFence) < 0)
+        return;
+
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
+            composition));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
+            handle, acquireFence, outErr));
+}
+
+void setColor(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+            layer, HWC2_COMPOSITION_SOLID_COLOR));
+    ASSERT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
+            layer, testLayer->getPlaneAlpha()));
+    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
+            layer, testLayer->getBlendMode()));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
+            testLayer->getColor(), outErr));
+}
+
+void setComposition(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    hwc2_composition_t composition = testLayer->getComposition();
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
+            composition, &err));
+    if (outErr) {
+        *outErr = err;
+        return;
+    }
+
+    if (composition != HWC2_COMPOSITION_SIDEBAND) {
+        EXPECT_EQ(err, HWC2_ERROR_NONE) << "returned wrong error code";
+    } else {
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
+                 << "returned wrong error code";
+    }
+}
+
+void setCursorPosition(Hwc2Test* test, hwc2_display_t display,
+        hwc2_layer_t layer, Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+            layer, HWC2_COMPOSITION_CURSOR));
+
+    const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+    EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
+            cursorPosition.left, cursorPosition.top, outErr));
+}
+
+void setDataspace(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerDataspace(display, layer,
+            testLayer->getDataspace(), outErr));
+}
+
+void setDisplayFrame(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerDisplayFrame(display, layer,
+            testLayer->getDisplayFrame(), outErr));
+}
+
+void setPlaneAlpha(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t *outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
+            testLayer->getBlendMode()));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display, layer,
+            testLayer->getPlaneAlpha(), outErr));
+}
+
+void setSourceCrop(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerSourceCrop(display, layer,
+            testLayer->getSourceCrop(), outErr));
+}
+
+void setSurfaceDamage(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerSurfaceDamage(display, layer,
+            testLayer->getSurfaceDamage(), outErr));
+}
+
+void setTransform(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerTransform(display, layer,
+            testLayer->getTransform(), outErr));
+}
+
+void setVisibleRegion(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display, layer,
+            testLayer->getVisibleRegion(), outErr));
+}
+
+void setZOrder(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
+            testLayer->getZOrder(), outErr));
+}
+
+bool advanceBlendMode(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceBlendMode();
+}
+
+bool advanceBuffer(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceComposition())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceColor(Hwc2TestLayer* testLayer)
+{
+    /* Color depends on blend mode so advance blend mode last so color is not
+     * force to update as often */
+    if (testLayer->advancePlaneAlpha())
+        return true;
+    if (testLayer->advanceColor())
+        return true;
+    return testLayer->advanceBlendMode();
+}
+
+bool advanceComposition(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceComposition();
+}
+
+bool advanceCursorPosition(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceCursorPosition();
+}
+
+bool advanceDataspace(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceDataspace();
+}
+
+bool advanceDisplayFrame(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceDisplayFrame();
+}
+
+bool advancePlaneAlpha(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advancePlaneAlpha();
+}
+
+bool advanceSourceCrop(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceSourceCrop())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceSurfaceDamage(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceSurfaceDamage())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceTransform(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceTransform();
+}
+
+bool advanceVisibleRegions(Hwc2TestLayers* testLayers)
+{
+    return testLayers->advanceVisibleRegions();
+}
+
+bool advanceClientTargetSupport(
+        Hwc2TestClientTargetSupport* testClientTargetSupport)
+{
+    return testClientTargetSupport->advance();
+}
+
+static const std::array<hwc2_function_descriptor_t, 42> requiredFunctions = {{
+    HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
+    HWC2_FUNCTION_CREATE_LAYER,
+    HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,
+    HWC2_FUNCTION_DESTROY_LAYER,
+    HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY,
+    HWC2_FUNCTION_DUMP,
+    HWC2_FUNCTION_GET_ACTIVE_CONFIG,
+    HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES,
+    HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT,
+    HWC2_FUNCTION_GET_COLOR_MODES,
+    HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE,
+    HWC2_FUNCTION_GET_DISPLAY_CONFIGS,
+    HWC2_FUNCTION_GET_DISPLAY_NAME,
+    HWC2_FUNCTION_GET_DISPLAY_REQUESTS,
+    HWC2_FUNCTION_GET_DISPLAY_TYPE,
+    HWC2_FUNCTION_GET_DOZE_SUPPORT,
+    HWC2_FUNCTION_GET_HDR_CAPABILITIES,
+    HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT,
+    HWC2_FUNCTION_GET_RELEASE_FENCES,
+    HWC2_FUNCTION_PRESENT_DISPLAY,
+    HWC2_FUNCTION_REGISTER_CALLBACK,
+    HWC2_FUNCTION_SET_ACTIVE_CONFIG,
+    HWC2_FUNCTION_SET_CLIENT_TARGET,
+    HWC2_FUNCTION_SET_COLOR_MODE,
+    HWC2_FUNCTION_SET_COLOR_TRANSFORM,
+    HWC2_FUNCTION_SET_CURSOR_POSITION,
+    HWC2_FUNCTION_SET_LAYER_BLEND_MODE,
+    HWC2_FUNCTION_SET_LAYER_BUFFER,
+    HWC2_FUNCTION_SET_LAYER_COLOR,
+    HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE,
+    HWC2_FUNCTION_SET_LAYER_DATASPACE,
+    HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME,
+    HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,
+    HWC2_FUNCTION_SET_LAYER_SOURCE_CROP,
+    HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE,
+    HWC2_FUNCTION_SET_LAYER_TRANSFORM,
+    HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION,
+    HWC2_FUNCTION_SET_LAYER_Z_ORDER,
+    HWC2_FUNCTION_SET_OUTPUT_BUFFER,
+    HWC2_FUNCTION_SET_POWER_MODE,
+    HWC2_FUNCTION_SET_VSYNC_ENABLED,
+    HWC2_FUNCTION_VALIDATE_DISPLAY,
+}};
+
+/* TESTCASE: Tests that the HWC2 supports all required functions. */
+TEST_F(Hwc2Test, GET_FUNCTION)
+{
+    for (hwc2_function_descriptor_t descriptor : requiredFunctions) {
+        hwc2_function_pointer_t pfn = getFunction(descriptor);
+        EXPECT_TRUE(pfn) << "failed to get function "
+                << getFunctionDescriptorName(descriptor);
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 fails to retrieve and invalid function. */
+TEST_F(Hwc2Test, GET_FUNCTION_invalid_function)
+{
+    hwc2_function_pointer_t pfn = getFunction(HWC2_FUNCTION_INVALID);
+    EXPECT_FALSE(pfn) << "failed to get invalid function";
+}
+
+/* TESTCASE: Tests that the HWC2 does not return an invalid capability. */
+TEST_F(Hwc2Test, GET_CAPABILITIES)
+{
+    std::vector<hwc2_capability_t> capabilities;
+
+    getCapabilities(&capabilities);
+
+    EXPECT_EQ(std::count(capabilities.begin(), capabilities.end(),
+            HWC2_CAPABILITY_INVALID), 0);
+}
+
+static const std::array<hwc2_callback_descriptor_t, 3> callbackDescriptors = {{
+    HWC2_CALLBACK_HOTPLUG,
+    HWC2_CALLBACK_REFRESH,
+    HWC2_CALLBACK_VSYNC,
+}};
+
+/* TESTCASE: Tests that the HWC2 can successfully register all required
+ * callback functions. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK)
+{
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+
+    for (auto descriptor : callbackDescriptors) {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
+                []() { return; }));
+    }
+}
+
+/* TESTCASE: Test that the HWC2 fails to register invalid callbacks. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK_bad_parameter)
+{
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_INVALID, data,
+            []() { return; }, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can register a callback with null data. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK_null_data)
+{
+    hwc2_callback_data_t data = nullptr;
+
+    for (auto descriptor : callbackDescriptors) {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
+                []() { return; }));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns the correct display type for each
+ * physical display. */
+TEST_F(Hwc2Test, GET_DISPLAY_TYPE)
+{
+    for (auto display : mDisplays) {
+        hwc2_display_type_t type;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type));
+        EXPECT_EQ(type, HWC2_DISPLAY_TYPE_PHYSICAL) << "failed to return"
+                " correct display type";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns an error when the display type of a bad
+ * display is requested. */
+TEST_F(Hwc2Test, GET_DISPLAY_TYPE_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_display_type_t type;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can create and destroy layers. */
+TEST_F(Hwc2Test, CREATE_DESTROY_LAYER)
+{
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer;
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot create a layer for a bad display */
+TEST_F(Hwc2Test, CREATE_LAYER_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_layer_t layer;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 will either support a large number of resources
+ * or will return no resources. */
+TEST_F(Hwc2Test, CREATE_LAYER_no_resources)
+{
+    const size_t layerCnt = 1000;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_layer_t> layers;
+
+        ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a layer for a bad display */
+TEST_F(Hwc2Test, DESTROY_LAYER_bad_display)
+{
+    hwc2_display_t badDisplay;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&badDisplay));
+
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer = 0;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destory a bad layer */
+TEST_F(Hwc2Test, DESTROY_LAYER_bad_layer)
+{
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX / 2, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 0, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX - 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer + 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+    }
+}
+
+static const std::array<hwc2_attribute_t, 2> requiredAttributes = {{
+    HWC2_ATTRIBUTE_WIDTH,
+    HWC2_ATTRIBUTE_HEIGHT,
+}};
+
+static const std::array<hwc2_attribute_t, 3> optionalAttributes = {{
+    HWC2_ATTRIBUTE_VSYNC_PERIOD,
+    HWC2_ATTRIBUTE_DPI_X,
+    HWC2_ATTRIBUTE_DPI_Y,
+}};
+
+/* TESTCASE: Tests that the HWC2 can return display attributes for a valid
+ * config. */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            int32_t value;
+
+            for (auto attribute : requiredAttributes) {
+                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                        attribute, &value));
+                EXPECT_GE(value, 0) << "missing required attribute "
+                        << getAttributeName(attribute) << " for config "
+                        << config;
+            }
+            for (auto attribute : optionalAttributes) {
+                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                        attribute, &value));
+            }
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will return a value of -1 for an invalid
+ * attribute */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_invalid_attribute)
+{
+    const hwc2_attribute_t attribute = HWC2_ATTRIBUTE_INVALID;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            int32_t value;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(value, -1) << "failed to return -1 for an invalid"
+                    " attribute for config " << config;
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad display */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_display)
+{
+    hwc2_display_t display;
+    const hwc2_config_t config = 0;
+    int32_t value;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    for (auto attribute : requiredAttributes) {
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
+                &value, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    }
+
+    for (auto attribute : optionalAttributes) {
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
+                &value, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad config */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_config)
+{
+    for (auto display : mDisplays) {
+        hwc2_config_t config;
+        int32_t value;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
+
+        for (auto attribute : requiredAttributes) {
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+
+        for (auto attribute : optionalAttributes) {
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will get display configs for active displays */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will not get display configs for bad displays */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_config_t> configs;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    EXPECT_TRUE(configs.empty()) << "returned configs for bad display";
+}
+
+/* TESTCASE: Tests that the HWC2 will return the same config list multiple
+ * times in a row. */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_same)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs1, configs2;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs1));
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs2));
+
+        EXPECT_TRUE(std::is_permutation(configs1.begin(), configs1.end(),
+                configs2.begin())) << "returned two different config sets";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return duplicate display configs */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_duplicate)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        std::unordered_set<hwc2_config_t> configsSet(configs.begin(),
+                configs.end());
+        EXPECT_EQ(configs.size(), configsSet.size()) << "returned duplicate"
+                " configs";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns the active config for a display */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            hwc2_config_t activeConfig;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+            ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig));
+
+            EXPECT_EQ(activeConfig, config) << "failed to get active config";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return an active config for a bad
+ * display. */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_config_t activeConfig;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 either begins with a valid active config
+ * or returns an error when getActiveConfig is called. */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_config)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+        hwc2_config_t activeConfig;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        if (configs.empty())
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
+        if (err == HWC2_ERROR_NONE) {
+            EXPECT_NE(std::count(configs.begin(), configs.end(),
+                    activeConfig), 0) << "active config is not found in "
+                    " configs for display";
+        } else {
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set every display config as an active
+ * config */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            EXPECT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an active config for a bad display */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_display)
+{
+    hwc2_display_t display;
+    const hwc2_config_t config = 0;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid active config */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_config)
+{
+    for (auto display : mDisplays) {
+        hwc2_config_t config;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
+
+        ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns a valid value for getDozeSupport. */
+TEST_F(Hwc2Test, GET_DOZE_SUPPORT)
+{
+    for (auto display : mDisplays) {
+        int32_t support = -1;
+
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+
+        EXPECT_TRUE(support == 0 || support == 1) << "invalid doze support value";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get doze support for a bad display. */
+TEST_F(Hwc2Test, GET_DOZE_SUPPORT_bad_display)
+{
+    hwc2_display_t display;
+    int32_t support = -1;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set all supported power modes */
+TEST_F(Hwc2Test, SET_POWER_MODE)
+{
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        int32_t support = -1;
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+        if (support != 1)
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a power mode for a bad display. */
+TEST_F(Hwc2Test, SET_POWER_MODE_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    int32_t support = -1;
+    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+    if (support != 1)
+        return;
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE_SUSPEND,
+            &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid power mode value. */
+TEST_F(Hwc2Test, SET_POWER_MODE_bad_parameter)
+{
+    for (auto display : mDisplays) {
+        hwc2_power_mode_t mode = static_cast<hwc2_power_mode_t>(
+                HWC2_POWER_MODE_DOZE_SUSPEND + 1);
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, mode, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code "
+                << mode;
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will return unsupported if it does not support
+ * an optional power mode. */
+TEST_F(Hwc2Test, SET_POWER_MODE_unsupported)
+{
+    for (auto display : mDisplays) {
+        int32_t support = -1;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+        if (support == 1)
+            return;
+
+        ASSERT_EQ(support, 0) << "invalid doze support value";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE,
+                &err));
+        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND, &err));
+        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) <<  "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set the same power mode multiple times. */
+TEST_F(Hwc2Test, SET_POWER_MODE_stress)
+{
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        int32_t support = -1;
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+        if (support != 1)
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can enable and disable vsync on active
+ * displays */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 issues a valid vsync callback. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_callback)
+{
+    for (auto display : mDisplays) {
+        hwc2_display_t receivedDisplay;
+        int64_t receivedTimestamp;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(enableVsync(display));
+
+        ASSERT_NO_FATAL_FAILURE(waitForVsync(&receivedDisplay,
+                &receivedTimestamp));
+
+        EXPECT_EQ(receivedDisplay, display) << "failed to get correct display";
+        EXPECT_GE(receivedTimestamp, 0) << "failed to get valid timestamp";
+
+        ASSERT_NO_FATAL_FAILURE(disableVsync(display));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot enable a vsync for a bad display */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+            []() { return; }));
+
+    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot enable an invalid vsync value */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_parameter)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_INVALID,
+                &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can enable and disable a vsync value multiple
+ * times. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_stress)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set a vsync enable value when the display
+ * is off and no callback is registered. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback_no_power)
+{
+    const uint secs = 1;
+
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        sleep(secs);
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set a vsync enable value when no callback
+ * is registered. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback)
+{
+    const uint secs = 1;
+
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        sleep(secs);
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns a display name for each display */
+TEST_F(Hwc2Test, GET_DISPLAY_NAME)
+{
+    for (auto display : mDisplays) {
+        std::string name;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return a display name for a bad
+ * display */
+TEST_F(Hwc2Test, GET_DISPLAY_NAME_bad_display)
+{
+    hwc2_display_t display;
+    std::string name;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set basic composition types. */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setComposition, advanceComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 can update a basic composition type on a
+ * layer. */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setComposition, advanceComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a composition type for a bad layer */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a bad composition type */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+                        layer, HWC2_COMPOSITION_INVALID, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            ::setCursorPosition, advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the cursor position of a layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            ::setCursorPosition, advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer when the
+ * composition type has not been set to HWC2_COMPOSITION_CURSOR. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_composition_type_unset)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
+                        cursorPosition.left, cursorPosition.top, outErr));
+            },
+
+            advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad
+ * display. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_layer_t layer = 0;
+    int32_t x = 0, y = 0;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setCursorPosition(display, layer, x, y, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display,
+                        badLayer, cursorPosition.left, cursorPosition.top,
+                        outErr));
+            }
+   ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set a blend mode value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setBlendMode, advanceBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 can update a blend mode value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setBlendMode, advanceBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a blend mode for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid blend mode. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
+                        layer, HWC2_BLEND_MODE_INVALID, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the buffer of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setBuffer, advanceBuffer));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the buffer of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setBuffer, advanceBuffer));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the buffer of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                buffer_handle_t handle = nullptr;
+                android::base::unique_fd acquireFence;
+
+                /* If there is not available buffer for the given buffer
+                 * properties, it should not fail this test case */
+                if (testLayer->getBuffer(&handle, &acquireFence) == 0) {
+                    *outErr = HWC2_ERROR_BAD_LAYER;
+                    return;
+                }
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, badLayer,
+                        handle, acquireFence, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set an invalid buffer for a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                buffer_handle_t handle = nullptr;
+                int32_t acquireFence = -1;
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
+                        handle, acquireFence, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the color of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setColor, advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the color of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setColor, advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the color of a layer when the
+ * composition type has not been set to HWC2_COMPOSITION_SOLID_COLOR. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_composition_type_unset)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Basic,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
+                        testLayer->getColor(), outErr));
+            },
+
+            advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the color of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, badLayer,
+                        testLayer->getColor(), outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the dataspace of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setDataspace, advanceDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the dataspace of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setDataspace, advanceDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a dataspace for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the display frame of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setDisplayFrame, advanceDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the display frame of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setDisplayFrame, advanceDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the display frame of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the plane alpha of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setPlaneAlpha, advancePlaneAlpha));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the plane alpha of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setPlaneAlpha, advancePlaneAlpha));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a plane alpha for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t *outErr) {
+
+                    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
+                            badLayer, testLayer->getPlaneAlpha(), outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the source crop of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setSourceCrop, advanceSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the source crop of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setSourceCrop, advanceSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the source crop of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the surface damage of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setSurfaceDamage, advanceSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the surface damage of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setSurfaceDamage, advanceSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the surface damage of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the transform value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setTransform, advanceTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the transform value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setTransform, advanceTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the transform for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the visible region of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Basic, 5,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayers* testLayers) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display,
+                        layer, testLayers->getVisibleRegion(layer)));
+            },
+
+            advanceVisibleRegions));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the visible region of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setVisibleRegion));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the z order of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Complete, 10,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayers* testLayers) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
+                        testLayers->getZOrder(layer)));
+            },
+
+            /* TestLayer z orders are set during the construction of TestLayers
+             * and cannot be updated. There is no need (or ability) to cycle
+             * through additional z order configurations. */
+            [] (Hwc2TestLayers* /*testLayers*/) {
+                return false;
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the z order of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_update)
+{
+    const std::vector<uint32_t> zOrders = { static_cast<uint32_t>(0),
+            static_cast<uint32_t>(1), static_cast<uint32_t>(UINT32_MAX / 4),
+            static_cast<uint32_t>(UINT32_MAX / 2),
+            static_cast<uint32_t>(UINT32_MAX) };
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            hwc2_layer_t layer;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+            ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+            for (uint32_t zOrder : zOrders) {
+                EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer, zOrder));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the z order of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setZOrder));
+}
+
+/* TESTCASE: Tests that the HWC2 can display a layer with basic property
+ * coverage */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                            << "wrong number of requests";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can display 5 layers with default coverage. */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_default_5)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                            << "wrong number of requests";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot validate a bad display */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    uint32_t numTypes, numRequests;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes, &numRequests,
+            &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can get display requests after validating a
+ * basic layer. */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                EXPECT_NO_FATAL_FAILURE(test->handleRequests(display, layers,
+                        numRequests));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get display requests from a bad display */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_display_request_t displayRequests;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<hwc2_layer_request_t> layerRequests;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequests,
+            &layers, &layerRequests, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get display requests from an non
+ * validated display. */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* layers) {
+
+                hwc2_display_request_t displayRequests;
+                std::vector<hwc2_layer_request_t> layerRequests;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getDisplayRequests(display,
+                        &displayRequests, layers, &layerRequests, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can get changed composition types after
+ * validating a basic layer. */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* testLayers) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                EXPECT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
+                        *testLayers, layers, numTypes));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get changed composition types from a bad
+ * display */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<hwc2_composition_t> types;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(getChangedCompositionTypes(display, &layers,
+            &types, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get changed composition types from an non
+ * validated display. */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* layers) {
+
+                std::vector<hwc2_composition_t> types;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getChangedCompositionTypes(
+                        display, layers, &types, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can accept display changes after validating a
+ * basic layer. */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* testLayers) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                ASSERT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
+                        *testLayers, layers, numTypes));
+
+                EXPECT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot accept display changes from a bad
+ * display */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(acceptDisplayChanges(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot accept display changes from an non
+ * validated display. */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* /*layers*/) {
+
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 supports client target with required values */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace()));
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get client target support for a bad
+ * display. */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_bad_display)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t /*display*/,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+                hwc2_display_t badDisplay;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(badDisplay,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace(), &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
+ * for a variety of client target values. */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_unsupported)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace(), &err));
+                EXPECT_TRUE(err == HWC2_ERROR_NONE
+                        || err == HWC2_ERROR_UNSUPPORTED)
+                        << "returned wrong error code";
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 can set a client target buffer for a basic
+ * layer. */
+TEST_F(Hwc2Test, SET_CLIENT_TARGET_basic)
+{
+    const android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+    const hwc_region_t damage = { };
+    const size_t layerCnt = 1;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            Area displayArea;
+            std::vector<hwc2_layer_t> layers;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+            ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
+
+            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+            Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Basic,
+                    displayArea);
+
+            if (!testLayers.optimizeLayouts())
+                continue;
+
+            Hwc2TestClientTarget testClientTarget;
+
+            do {
+                std::set<hwc2_layer_t> clientLayers;
+                std::set<hwc2_layer_t> clearLayers;
+                uint32_t numTypes, numRequests;
+                bool hasChanges, skip;
+                bool flipClientTarget;
+                buffer_handle_t handle;
+                int32_t acquireFence;
+
+                ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                        &testLayers, &skip));
+                if (skip)
+                    continue;
+
+                ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
+                        testLayers, layers, numTypes, &clientLayers));
+                ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
+                        numRequests, &clearLayers, &flipClientTarget));
+                ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
+                        clearLayers, flipClientTarget, displayArea, &handle,
+                        &acquireFence), 0);
+                EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle,
+                        acquireFence, dataspace, damage));
+
+                if (acquireFence >= 0)
+                    close(acquireFence);
+
+            } while (testLayers.advance());
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+        }
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a client target for a bad display. */
+TEST_F(Hwc2Test, SET_CLIENT_TARGET_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    const Area displayArea = {0, 0};
+    Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Default, displayArea);
+    std::set<hwc2_layer_t> clientLayers;
+    std::set<hwc2_layer_t> flipClientTargetLayers;
+    bool flipClientTarget = true;
+    const android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+    const hwc_region_t damage = { };
+    buffer_handle_t handle;
+    int32_t acquireFence;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    Hwc2TestClientTarget testClientTarget;
+
+    ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
+            flipClientTargetLayers, flipClientTarget, displayArea, &handle,
+            &acquireFence), 0);
+
+    EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
+            dataspace, damage, &err));
+
+    if (acquireFence >= 0)
+        close(acquireFence);
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 default layer. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 3 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_3)
+{
+    const size_t layerCnt = 3;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 4 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_4)
+{
+    const size_t layerCnt = 4;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 5 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_5)
+{
+    const size_t layerCnt = 5;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 6 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_6)
+{
+    const size_t layerCnt = 6;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * blend mode. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * blend mode. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * buffer. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_buffer_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * color. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_color_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * color. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_color_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * composition. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_composition_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * cursor. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * cursor. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * dataspace. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_dataspace_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 3 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_3)
+{
+    const size_t layerCnt = 3;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 4 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_4)
+{
+    const size_t layerCnt = 4;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * plane alpha. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * plane alpha. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * source crop. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * source crop. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * surface damage. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_surface_damage_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::SurfaceDamage, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * transform. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * transform. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * basic. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_basic_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot present a bad display.  */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    int32_t presentFence;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplay(display, &presentFence, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot present an unvalidated display. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& /*layers*/,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                int32_t presentFence;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
+                        HWC2_POWER_MODE_ON));
+                ASSERT_NO_FATAL_FAILURE(test->enableVsync(display));
+
+                ASSERT_NO_FATAL_FAILURE(test->waitForVsync());
+
+                ASSERT_NO_FATAL_FAILURE(test->presentDisplay(display,
+                        &presentFence, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(test->disableVsync(display));
+                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
+                        HWC2_POWER_MODE_OFF));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get release fences from a bad display. */
+TEST_F(Hwc2Test, GET_RELEASE_FENCES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<int32_t> fences;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+static const std::array<android_color_mode, 9> androidColorModes = {{
+    HAL_COLOR_MODE_NATIVE,
+    HAL_COLOR_MODE_STANDARD_BT601_625,
+    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED,
+    HAL_COLOR_MODE_STANDARD_BT601_525,
+    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED,
+    HAL_COLOR_MODE_STANDARD_BT709,
+    HAL_COLOR_MODE_DCI_P3,
+    HAL_COLOR_MODE_SRGB,
+    HAL_COLOR_MODE_ADOBE_RGB,
+}};
+
+/* TESTCASE: Tests that the HWC2 can get the color modes for a display. The
+ * display must support HAL_COLOR_MODE_NATIVE */
+TEST_F(Hwc2Test, GET_COLOR_MODES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                std::vector<android_color_mode_t> colorModes;
+
+                ASSERT_NO_FATAL_FAILURE(test->getColorModes(display,
+                        &colorModes));
+
+                EXPECT_NE(std::count(colorModes.begin(), colorModes.end(),
+                        HAL_COLOR_MODE_NATIVE), 0) << "all displays"
+                        " must support HAL_COLOR_MODE_NATIVE";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get color modes from a bad display. */
+TEST_F(Hwc2Test, GET_COLOR_MODES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<android_color_mode_t> colorModes;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getColorModes(display, &colorModes, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set the required color mode on a display. */
+TEST_F(Hwc2Test, SET_COLOR_MODES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_mode_t colorMode = HAL_COLOR_MODE_NATIVE;
+
+                EXPECT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a color mode on a bad display. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_bad_display)
+{
+    hwc2_display_t display;
+    const android_color_mode_t colorMode = HAL_COLOR_MODE_NATIVE;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setColorMode(display, colorMode, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid color mode. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_mode_t colorMode =
+                        static_cast<android_color_mode_t>(-1);
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode,
+                        &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
+ * for all valid color modes. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_unsupported)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                for (auto colorMode : androidColorModes) {
+                    hwc2_error_t err = HWC2_ERROR_NONE;
+
+                    ASSERT_NO_FATAL_FAILURE(test->setColorMode(display,
+                            colorMode, &err));
+
+                    EXPECT_TRUE(err == HWC2_ERROR_NONE
+                            || err == HWC2_ERROR_UNSUPPORTED)
+                            << "returned wrong error code";
+                }
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 gets the HDR capabilities for a display and
+ * test if they are valid. */
+TEST_F(Hwc2Test, GET_HDR_CAPABILITIES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                std::vector<android_hdr_t> hdrCapabilities;
+                float maxLuminance, maxAverageLuminance, minLuminance;
+
+                EXPECT_NO_FATAL_FAILURE(test->getHdrCapabilities(display,
+                        &hdrCapabilities, &maxLuminance, &maxAverageLuminance,
+                        &minLuminance));
+
+                if (hdrCapabilities.empty())
+                    return;
+
+                EXPECT_GE(maxLuminance, maxAverageLuminance);
+                EXPECT_GE(maxAverageLuminance, minLuminance);
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get hdr capabilities from a bad display */
+TEST_F(Hwc2Test, GET_HDR_CAPABILITIES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<android_hdr_t> hdrCapabilities;
+    float maxLuminance, maxAverageLuminance, minLuminance;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getHdrCapabilities(display, &hdrCapabilities,
+            &maxLuminance, &maxAverageLuminance, &minLuminance, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+static const std::array<float, 16> identityMatrix = {{
+    1.0,  0.0,  0.0,  0.0,
+    0.0,  1.0,  0.0,  0.0,
+    0.0,  0.0,  1.0,  0.0,
+    0.0,  0.0,  0.0,  1.0,
+}};
+
+/* Values for the color transform matrices were precomputed using the source code
+ * in surfaceflinger/Effects/Daltonizer.cpp. */
+
+static const std::array<const std::array<float, 16>, 5> exampleMatrices = {{
+    identityMatrix,
+    /* Converts RGB color to the XYZ space */
+    {{ 0.4124, 0.2126, 0.0193, 0,
+       0.3576, 0.7152, 0.1192, 0,
+       0.1805, 0.0722, 0.9505, 0,
+       0     , 0     , 0     , 1 }},
+    /* Protanomaly */
+    {{ 0.068493,  0.931506,  0,  0,
+       0.068493,  0.931507,  0,  0,
+       0.013626, -0.013626,  1,  0,
+       0,         0,         0,  1 }},
+    /* Deuteranomaly */
+    {{ 0.288299, 0.711701,  0,  0,
+       0.052709, 0.947291,  0,  0,
+      -0.257912, 0.257912,  1,  0,
+       0,        0,         0,  1 }},
+    /* Tritanomaly */
+    {{ 1, -0.805712, 0.805712,  0,
+       0,  0.378838, 0.621162,  0,
+       0,  0.104823, 0.895177,  0,
+       0,  0,        0,         1 }},
+}};
+
+/* TESTCASE: Tests that the HWC2 can set the identity color transform */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                        identityMatrix, HAL_COLOR_TRANSFORM_IDENTITY));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the color transform for a bad
+ * display. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setColorTransform(display, identityMatrix,
+            HAL_COLOR_TRANSFORM_IDENTITY, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid color transform. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_transform_t hint =
+                        static_cast<android_color_transform_t>(-1);
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                        identityMatrix, hint, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set an arbitrary color matrix. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_arbitrary_matrix)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_transform_t hint =
+                        HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+
+                for (const std::array<float, 16>& matrix : exampleMatrices) {
+                    EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                            matrix, hint));
+                }
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 create an destory virtual displays. */
+TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* /*test*/, hwc2_display_t /*display*/,
+                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) { }));
+}
+
+/* TESTCASE: Tests that the HWC2 can create and destroy multiple virtual
+ * displays. */
+TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY_multiple)
+{
+    Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
+    std::vector<hwc2_display_t> displays;
+
+    do {
+        const UnsignedArea& dimension =
+                testVirtualDisplay.getDisplayDimension();
+        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+        hwc2_display_t display;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
+                dimension.height, &desiredFormat, &display, &err));
+
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
+                || err == HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+        EXPECT_GE(desiredFormat, 0) << "invalid format";
+
+        if (err == HWC2_ERROR_NONE)
+            displays.push_back(display);
+
+    } while (testVirtualDisplay.advance());
+
+    for (hwc2_display_t display : displays) {
+        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a bad virtual displays.  */
+TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
+TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
+{
+    hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT)
+{
+    uint32_t maxCnt;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
+}
+
+/* TESTCASE: Tests that the HWC2 returns the same max virtual display count for
+ * each call. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_duplicate)
+{
+    uint32_t maxCnt1, maxCnt2;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt1));
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt2));
+
+    EXPECT_EQ(maxCnt1, maxCnt2) << "returned two different max virtual display"
+            " counts";
+}
+
+/* TESTCASE: Tests that the HWC2 can create the max number of virtual displays
+ * that it reports. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_create_max)
+{
+    std::vector<hwc2_display_t> displays;
+    uint32_t maxCnt;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
+
+    while (displays.size() < maxCnt) {
+        uint32_t width = 1920, height = 1080;
+        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+        hwc2_display_t display;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(width, height,
+                    &desiredFormat, &display, &err));
+
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
+                << "returned wrong error code";
+        if (err != HWC2_ERROR_NONE)
+            break;
+
+        displays.push_back(display);
+    }
+
+    for (hwc2_display_t display : displays) {
+        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set an output buffer for a virtual
+ * display. */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
+
+                buffer_handle_t handle;
+                android::base::unique_fd acquireFence;
+
+                if (testVirtualDisplay->getBuffer(&handle, &acquireFence) >= 0)
+                    EXPECT_NO_FATAL_FAILURE(test->setOutputBuffer(display,
+                            handle, acquireFence));
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an output buffer for a bad display */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_display)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t /*display*/,
+                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
+
+                hwc2_display_t badDisplay;
+                buffer_handle_t handle;
+                android::base::unique_fd acquireFence;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
+
+                if (testVirtualDisplay->getBuffer(&handle, &acquireFence) < 0)
+                    return;
+
+                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(badDisplay,
+                        handle, acquireFence, &err));
+                EXPECT_TRUE(err == HWC2_ERROR_BAD_DISPLAY)
+                        << "returned wrong error code";
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid output buffer. */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) {
+
+                const buffer_handle_t handle = nullptr;
+                uint32_t releaseFence = -1;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(display, handle,
+                        releaseFence, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an output buffer for non virtual
+ * display */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_unsupported)
+{
+    for (auto display : mDisplays) {
+        Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
+
+        do {
+            buffer_handle_t handle;
+            android::base::unique_fd acquireFence;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            if (testVirtualDisplay.getBuffer(&handle, &acquireFence) < 0)
+                continue;
+
+            ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, handle,
+                    acquireFence, &err));
+            EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+
+        } while (testVirtualDisplay.advance());
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can dump debug information. */
+TEST_F(Hwc2Test, DUMP)
+{
+    std::string buffer;
+
+    ASSERT_NO_FATAL_FAILURE(dump(&buffer));
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
new file mode 100644
index 0000000..1d3a1d3
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mutex>
+#include <array>
+#include <sstream>
+#include <algorithm>
+
+#include <gui/Surface.h>
+#include <gui/BufferItemConsumer.h>
+
+#include <ui/GraphicBuffer.h>
+#include <math/vec4.h>
+
+#include <GLES3/gl3.h>
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestLayers.h"
+
+using namespace android;
+
+/* Returns a fence from egl */
+typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
+
+/* Returns fence to fence generator */
+static void setFence(int32_t fence, void* fenceGenerator);
+
+
+/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
+ * away. The fences are sent to the requester via a callback */
+class Hwc2TestSurfaceManager {
+public:
+    /* Listens for a new frame, detaches the buffer and returns the fence
+     * through saved callback. */
+    class BufferListener : public ConsumerBase::FrameAvailableListener {
+    public:
+        BufferListener(sp<IGraphicBufferConsumer> consumer,
+                FenceCallback callback, void* callbackArgs)
+            : mConsumer(consumer),
+              mCallback(callback),
+              mCallbackArgs(callbackArgs) { }
+
+        void onFrameAvailable(const BufferItem& /*item*/)
+        {
+            BufferItem item;
+
+            if (mConsumer->acquireBuffer(&item, 0))
+                return;
+            if (mConsumer->detachBuffer(item.mSlot))
+                return;
+
+            mCallback(item.mFence->dup(), mCallbackArgs);
+        }
+
+    private:
+        sp<IGraphicBufferConsumer> mConsumer;
+        FenceCallback mCallback;
+        void* mCallbackArgs;
+    };
+
+    /* Creates a buffer listener that waits on a new frame from the buffer
+     * queue. */
+    void initialize(const Area& bufferArea, android_pixel_format_t format,
+            FenceCallback callback, void* callbackArgs)
+    {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+
+        consumer->setDefaultBufferSize(bufferArea.width, bufferArea.height);
+        consumer->setDefaultBufferFormat(format);
+
+        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+        mListener = new BufferListener(consumer, callback, callbackArgs);
+        mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+        mSurface = new Surface(producer, true);
+    }
+
+    /* Used by Egl manager. The surface is never displayed. */
+    sp<Surface> getSurface() const
+    {
+        return mSurface;
+    }
+
+private:
+    sp<BufferItemConsumer> mBufferItemConsumer;
+    sp<BufferListener> mListener;
+    /* Used by Egl manager. The surface is never displayed */
+    sp<Surface> mSurface;
+};
+
+
+/* Used to generate valid fences. It is not possible to create a dummy sync
+ * fence for testing. Egl can generate buffers along with a valid fence.
+ * The buffer cannot be guaranteed to be the same format across all devices so
+ * a CPU filled buffer is used instead. The Egl fence is used along with the
+ * CPU filled buffer. */
+class Hwc2TestEglManager {
+public:
+    Hwc2TestEglManager()
+        : mEglDisplay(EGL_NO_DISPLAY),
+          mEglSurface(EGL_NO_SURFACE),
+          mEglContext(EGL_NO_CONTEXT) { }
+
+    ~Hwc2TestEglManager()
+    {
+        cleanup();
+    }
+
+    int initialize(sp<Surface> surface)
+    {
+        mSurface = surface;
+
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+        EGLint major;
+        EGLint minor;
+        if (!eglInitialize(mEglDisplay, &major, &minor)) {
+            ALOGW("Could not initialize EGL");
+            return false;
+        }
+
+        /* We're going to use a 1x1 pbuffer surface later on
+         * The configuration distance doesn't really matter for what we're
+         * trying to do */
+        EGLint configAttrs[] = {
+                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                EGL_RED_SIZE, 8,
+                EGL_GREEN_SIZE, 8,
+                EGL_BLUE_SIZE, 8,
+                EGL_ALPHA_SIZE, 0,
+                EGL_DEPTH_SIZE, 24,
+                EGL_STENCIL_SIZE, 0,
+                EGL_NONE
+        };
+
+        EGLConfig configs[1];
+        EGLint configCnt;
+        if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1,
+                &configCnt)) {
+            ALOGW("Could not select EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        if (configCnt <= 0) {
+            ALOGW("Could not find EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        /* These objects are initialized below but the default "null" values are
+         * used to cleanup properly at any point in the initialization sequence */
+        EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+        mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT,
+                attrs);
+        if (mEglContext == EGL_NO_CONTEXT) {
+            ALOGW("Could not create EGL context");
+            cleanup();
+            return false;
+        }
+
+        EGLint surfaceAttrs[] = { EGL_NONE };
+        mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0],
+                mSurface.get(), surfaceAttrs);
+        if (mEglSurface == EGL_NO_SURFACE) {
+            ALOGW("Could not create EGL surface");
+            cleanup();
+            return false;
+        }
+
+        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            ALOGW("Could not change current EGL context");
+            cleanup();
+            return false;
+        }
+
+        return true;
+    }
+
+    void makeCurrent() const
+    {
+        eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
+    }
+
+    void present() const
+    {
+        eglSwapBuffers(mEglDisplay, mEglSurface);
+    }
+
+private:
+    void cleanup()
+    {
+        if (mEglDisplay == EGL_NO_DISPLAY)
+            return;
+        if (mEglSurface != EGL_NO_SURFACE)
+            eglDestroySurface(mEglDisplay, mEglSurface);
+        if (mEglContext != EGL_NO_CONTEXT)
+            eglDestroyContext(mEglDisplay, mEglContext);
+
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                EGL_NO_CONTEXT);
+        eglReleaseThread();
+        eglTerminate(mEglDisplay);
+    }
+
+    sp<Surface> mSurface;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+};
+
+
+static const std::array<vec2, 4> triangles = {{
+    {  1.0f,  1.0f },
+    { -1.0f,  1.0f },
+    {  1.0f, -1.0f },
+    { -1.0f, -1.0f },
+}};
+
+class Hwc2TestFenceGenerator {
+public:
+
+    Hwc2TestFenceGenerator()
+    {
+        mSurfaceManager.initialize({1, 1}, HAL_PIXEL_FORMAT_RGBA_8888,
+                setFence, this);
+
+        if (!mEglManager.initialize(mSurfaceManager.getSurface()))
+            return;
+
+        mEglManager.makeCurrent();
+
+        glClearColor(0.0, 0.0, 0.0, 1.0);
+        glEnableVertexAttribArray(0);
+    }
+
+    ~Hwc2TestFenceGenerator()
+    {
+        if (mFence >= 0)
+            close(mFence);
+        mFence = -1;
+
+        mEglManager.makeCurrent();
+    }
+
+    /* It is not possible to simply generate a fence. The easiest way is to
+     * generate a buffer using egl and use the associated fence. The buffer
+     * cannot be guaranteed to be a certain format across all devices using this
+     * method. Instead the buffer is generated using the CPU */
+    int32_t get()
+    {
+        if (mFence >= 0) {
+            return dup(mFence);
+        }
+
+        std::unique_lock<std::mutex> lock(mMutex);
+
+        /* If the pending is still set to false and times out, we cannot recover.
+         * Set an error and return */
+        while (mPending != false) {
+            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
+                return -ETIME;
+        }
+
+        /* Generate a fence. The fence will be returned through the setFence
+         * callback */
+        mEglManager.makeCurrent();
+
+        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, triangles.data());
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        mEglManager.present();
+
+        /* Wait for the setFence callback */
+        while (mPending != true) {
+            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
+                return -ETIME;
+        }
+
+        mPending = false;
+
+        return dup(mFence);
+    }
+
+    /* Callback that sets the fence */
+    void set(int32_t fence)
+    {
+        mFence = fence;
+        mPending = true;
+
+        mCv.notify_all();
+    }
+
+private:
+
+    Hwc2TestSurfaceManager mSurfaceManager;
+    Hwc2TestEglManager mEglManager;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+
+    int32_t mFence = -1;
+    bool mPending = false;
+};
+
+
+static void setFence(int32_t fence, void* fenceGenerator)
+{
+    static_cast<Hwc2TestFenceGenerator*>(fenceGenerator)->set(fence);
+}
+
+
+/* Sets the pixel of a buffer given the location, format, stride and color.
+ * Currently only supports RGBA_8888 */
+static void setColor(int32_t x, int32_t y,
+        android_pixel_format_t format, uint32_t stride, uint8_t* img, uint8_t r,
+        uint8_t g, uint8_t b, uint8_t a)
+{
+       switch (format) {
+       case HAL_PIXEL_FORMAT_RGBA_8888:
+           img[(y * stride + x) * 4 + 0] = r;
+           img[(y * stride + x) * 4 + 1] = g;
+           img[(y * stride + x) * 4 + 2] = b;
+           img[(y * stride + x) * 4 + 3] = a;
+           break;
+       default:
+           break;
+       }
+}
+
+Hwc2TestBuffer::Hwc2TestBuffer()
+    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
+
+Hwc2TestBuffer::~Hwc2TestBuffer() = default;
+
+/* When the buffer changes sizes, save the new size and invalidate the current
+ * buffer */
+void Hwc2TestBuffer::updateBufferArea(const Area& bufferArea)
+{
+    if (mBufferArea.width == bufferArea.width
+            && mBufferArea.height == bufferArea.height)
+        return;
+
+    mBufferArea.width = bufferArea.width;
+    mBufferArea.height = bufferArea.height;
+
+    mValidBuffer = false;
+}
+
+/* Returns a valid buffer handle and fence. The handle is filled using the CPU
+ * to ensure the correct format across all devices. The fence is created using
+ * egl. */
+int Hwc2TestBuffer::get(buffer_handle_t* outHandle, int32_t* outFence)
+{
+    if (mBufferArea.width == -1 || mBufferArea.height == -1)
+        return -EINVAL;
+
+    /* If the current buffer is valid, the previous buffer can be reused.
+     * Otherwise, create new buffer */
+    if (!mValidBuffer) {
+        int ret = generateBuffer();
+        if (ret)
+            return ret;
+    }
+
+    *outFence = mFenceGenerator->get();
+    *outHandle = mHandle;
+
+    mValidBuffer = true;
+
+    return 0;
+}
+
+/* CPU fills a buffer to guarantee the correct buffer format across all
+ * devices */
+int Hwc2TestBuffer::generateBuffer()
+{
+    /* Create new graphic buffer with correct dimensions */
+    mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
+            mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
+            "hwc2_test_buffer");
+    int ret = mGraphicBuffer->initCheck();
+    if (ret) {
+        return ret;
+    }
+    if (!mGraphicBuffer->handle) {
+        return -EINVAL;
+    }
+
+    /* Locks the buffer for writing */
+    uint8_t* img;
+    mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+    uint32_t stride = mGraphicBuffer->getStride();
+
+    /* Iterate from the top row of the buffer to the bottom row */
+    for (int32_t y = 0; y < mBufferArea.height; y++) {
+
+        /* Will be used as R, G and B values for pixel colors */
+        uint8_t max = 255;
+        uint8_t min = 0;
+
+        /* Divide the rows into 3 sections. The first section will contain
+         * the lighest colors. The last section will contain the darkest
+         * colors. */
+        if (y < mBufferArea.height * 1.0 / 3.0) {
+            min = 255 / 2;
+        } else if (y >= mBufferArea.height * 2.0 / 3.0) {
+            max = 255 / 2;
+        }
+
+        /* Divide the columns into 3 sections. The first section is red,
+         * the second is green and the third is blue */
+        int32_t x = 0;
+        for (; x < mBufferArea.width / 3; x++) {
+            setColor(x, y, mFormat, stride, img, max, min, min, 255);
+        }
+
+        for (; x < mBufferArea.width * 2 / 3; x++) {
+            setColor(x, y, mFormat, stride, img, min, max, min, 255);
+        }
+
+        for (; x < mBufferArea.width; x++) {
+            setColor(x, y, mFormat, stride, img, min, min, max, 255);
+        }
+    }
+
+    /* Unlock the buffer for reading */
+    mGraphicBuffer->unlock();
+
+    mHandle = mGraphicBuffer->handle;
+
+    return 0;
+}
+
+
+Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer()
+    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
+
+Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
+
+/* Generates a client target buffer using the layers assigned for client
+ * composition. Takes into account the individual layer properties such as
+ * transform, blend mode, source crop, etc. */
+int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
+        int32_t* outFence, const Area& bufferArea,
+        const Hwc2TestLayers* testLayers,
+        const std::set<hwc2_layer_t>* clientLayers,
+        const std::set<hwc2_layer_t>* clearLayers)
+{
+    /* Create new graphic buffer with correct dimensions */
+    mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
+            mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
+            "hwc2_test_buffer");
+    int ret = mGraphicBuffer->initCheck();
+    if (ret) {
+        return ret;
+    }
+    if (!mGraphicBuffer->handle) {
+        return -EINVAL;
+    }
+
+    uint8_t* img;
+    mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+    uint32_t stride = mGraphicBuffer->getStride();
+
+    float bWDiv3 = bufferArea.width / 3;
+    float bW2Div3 = bufferArea.width * 2 / 3;
+    float bHDiv3 = bufferArea.height / 3;
+    float bH2Div3 = bufferArea.height * 2 / 3;
+
+    /* Cycle through every pixel in the buffer and determine what color it
+     * should be. */
+    for (int32_t y = 0; y < bufferArea.height; y++) {
+        for (int32_t x = 0; x < bufferArea.width; x++) {
+
+            uint8_t r = 0, g = 0, b = 0;
+            float a = 0.0f;
+
+            /* Cycle through each client layer from back to front and
+             * update the pixel color. */
+            for (auto layer = clientLayers->rbegin();
+                    layer != clientLayers->rend(); ++layer) {
+
+                const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
+
+                float dfL = df.left;
+                float dfT = df.top;
+                float dfR = df.right;
+                float dfB = df.bottom;
+
+                /* If the pixel location falls outside of the layer display
+                 * frame, skip the layer. */
+                if (x < dfL || x >= dfR || y < dfT || y >= dfB)
+                    continue;
+
+                /* If the device has requested the layer be clear, clear
+                 * the pixel and continue. */
+                if (clearLayers->count(*layer) != 0) {
+                    r = 0;
+                    g = 0;
+                    b = 0;
+                    a = 0.0f;
+                    continue;
+                }
+
+                float planeAlpha = testLayers->getPlaneAlpha(*layer);
+
+                /* If the layer is a solid color, fill the color and
+                 * continue. */
+                if (testLayers->getComposition(*layer)
+                        == HWC2_COMPOSITION_SOLID_COLOR) {
+                    const auto color = testLayers->getColor(*layer);
+                    r = color.r;
+                    g = color.g;
+                    b = color.b;
+                    a = color.a * planeAlpha;
+                    continue;
+                }
+
+                float xPos = x;
+                float yPos = y;
+
+                hwc_transform_t transform = testLayers->getTransform(*layer);
+
+                float dfW = dfR - dfL;
+                float dfH = dfB - dfT;
+
+                /* If a layer has a transform, find which location on the
+                 * layer will end up in the current pixel location. We
+                 * can calculate the color of the current pixel using that
+                 * location. */
+                if (transform > 0) {
+                    /* Change origin to be the center of the layer. */
+                    xPos = xPos - dfL - dfW / 2.0;
+                    yPos = yPos - dfT - dfH / 2.0;
+
+                    /* Flip Horizontal by reflecting across the y axis. */
+                    if (transform & HWC_TRANSFORM_FLIP_H)
+                        xPos = -xPos;
+
+                    /* Flip vertical by reflecting across the x axis. */
+                    if (transform & HWC_TRANSFORM_FLIP_V)
+                        yPos = -yPos;
+
+                    /* Rotate 90 by using a basic linear algebra rotation
+                     * and scaling the result so the display frame remains
+                     * the same. For example, a buffer of size 100x50 should
+                     * rotate 90 degress but remain the same dimension
+                     * (100x50) at the end of the transformation. */
+                    if (transform & HWC_TRANSFORM_ROT_90) {
+                        float tmp = xPos;
+                        xPos = -yPos * dfW / dfH;
+                        yPos = tmp * dfH / dfW;
+                    }
+
+                    /* Change origin back to the top left corner of the
+                     * layer. */
+                    xPos = xPos + dfL + dfW / 2.0;
+                    yPos = yPos + dfT + dfH / 2.0;
+                }
+
+                hwc_frect_t sc = testLayers->getSourceCrop(*layer);
+                float scL = sc.left, scT = sc.top;
+
+                float dfWDivScW = dfW / (sc.right - scL);
+                float dfHDivScH = dfH / (sc.bottom - scT);
+
+                float max = 255, min = 0;
+
+                /* Choose the pixel color. Similar to generateBuffer,
+                 * each layer will be divided into 3x3 colors. Because
+                 * both the source crop and display frame must be taken into
+                 * account, the formulas are more complicated.
+                 *
+                 * If the source crop and display frame were not taken into
+                 * account, we would simply divide the buffer into three
+                 * sections by height. Each section would get one color.
+                 * For example the formula for the first section would be:
+                 *
+                 * if (yPos < bufferArea.height / 3)
+                 *        //Select first section color
+                 *
+                 * However the pixel color is chosen based on the source
+                 * crop and displayed based on the display frame.
+                 *
+                 * If the display frame top was 0 and the source crop height
+                 * and display frame height were the same. The only factor
+                 * would be the source crop top. To calculate the new
+                 * section boundary, the section boundary would be moved up
+                 * by the height of the source crop top. The formula would
+                 * be:
+                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top)
+                 *        //Select first section color
+                 *
+                 * If the display frame top could also vary but source crop
+                 * and display frame heights were the same, the formula
+                 * would be:
+                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top
+                 *              + displayFrameTop)
+                 *        //Select first section color
+                 *
+                 * If the heights were not the same, the conversion between
+                 * the source crop and display frame dimensions must be
+                 * taken into account. The formula would be:
+                 * if (yPos < ((bufferArea.height / 3) - sourceCrop.top)
+                 *              * displayFrameHeight / sourceCropHeight
+                 *              + displayFrameTop)
+                 *        //Select first section color
+                 */
+                if (yPos < ((bHDiv3) - scT) * dfHDivScH + dfT) {
+                    min = 255 / 2;
+                } else if (yPos >= ((bH2Div3) - scT) * dfHDivScH + dfT) {
+                    max = 255 / 2;
+                }
+
+                uint8_t rCur = min, gCur = min, bCur = min;
+                float aCur = 1.0f;
+
+                /* This further divides the color sections from 3 to 3x3.
+                 * The math behind it follows the same logic as the previous
+                 * comment */
+                if (xPos < ((bWDiv3) - scL) * (dfWDivScW) + dfL) {
+                    rCur = max;
+                } else if (xPos < ((bW2Div3) - scL) * (dfWDivScW) + dfL) {
+                    gCur = max;
+                } else {
+                    bCur = max;
+                }
+
+
+                /* Blend the pixel color with the previous layers' pixel
+                 * colors using the plane alpha and blend mode. The final
+                 * pixel color is chosen using the plane alpha and blend
+                 * mode formulas found in hwcomposer2.h */
+                hwc2_blend_mode_t blendMode = testLayers->getBlendMode(*layer);
+
+                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
+                    rCur *= planeAlpha;
+                    gCur *= planeAlpha;
+                    bCur *= planeAlpha;
+                }
+
+                aCur *= planeAlpha;
+
+                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
+                    r = rCur + r * (1.0 - aCur);
+                    g = gCur + g * (1.0 - aCur);
+                    b = bCur + b * (1.0 - aCur);
+                    a = aCur + a * (1.0 - aCur);
+                } else if (blendMode == HWC2_BLEND_MODE_COVERAGE) {
+                    r = rCur * aCur + r * (1.0 - aCur);
+                    g = gCur * aCur + g * (1.0 - aCur);
+                    b = bCur * aCur + b * (1.0 - aCur);
+                    a = aCur * aCur + a * (1.0 - aCur);
+                } else {
+                    r = rCur;
+                    g = gCur;
+                    b = bCur;
+                    a = aCur;
+                }
+            }
+
+            /* Set the pixel color */
+            setColor(x, y, mFormat, stride, img, r, g, b, a * 255);
+        }
+    }
+
+    mGraphicBuffer->unlock();
+
+    *outFence = mFenceGenerator->get();
+    *outHandle = mGraphicBuffer->handle;
+
+    return 0;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
new file mode 100644
index 0000000..b2b3a66
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_BUFFER_H
+#define _HWC2_TEST_BUFFER_H
+
+#include <android-base/unique_fd.h>
+#include <set>
+
+#include <hardware/hwcomposer2.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "Hwc2TestProperties.h"
+
+class Hwc2TestFenceGenerator;
+class Hwc2TestLayers;
+
+class Hwc2TestBuffer {
+public:
+    Hwc2TestBuffer();
+    ~Hwc2TestBuffer();
+
+    void updateBufferArea(const Area& bufferArea);
+
+    int  get(buffer_handle_t* outHandle, int32_t* outFence);
+
+protected:
+    int generateBuffer();
+
+    android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
+
+    Area mBufferArea = {-1, -1};
+    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+
+    bool mValidBuffer = false;
+    buffer_handle_t mHandle = nullptr;
+};
+
+
+class Hwc2TestClientTargetBuffer {
+public:
+    Hwc2TestClientTargetBuffer();
+    ~Hwc2TestClientTargetBuffer();
+
+    int  get(buffer_handle_t* outHandle, int32_t* outFence,
+            const Area& bufferArea, const Hwc2TestLayers* testLayers,
+            const std::set<hwc2_layer_t>* clientLayers,
+            const std::set<hwc2_layer_t>* clearLayers);
+
+protected:
+    android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
+
+    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+};
+
+#endif /* ifndef _HWC2_TEST_BUFFER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
new file mode 100644
index 0000000..6925492
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+
+#include <ui/Rect.h>
+
+#include "Hwc2TestClientTarget.h"
+
+int Hwc2TestClientTarget::getBuffer(const Hwc2TestLayers& testLayers,
+        const std::set<hwc2_layer_t>& clientLayers,
+        const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
+        const Area& displayArea, buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    if (!flipClientTarget) {
+        bool needsClientTarget = false;
+
+        for (auto clientLayer : clientLayers) {
+            if (testLayers.getVisibleRegion(clientLayer).numRects > 0) {
+                needsClientTarget = true;
+                break;
+            }
+        }
+
+        if (!needsClientTarget) {
+           *outHandle = nullptr;
+           *outAcquireFence = -1;
+           return 0;
+        }
+    }
+
+    return mBuffer.get(outHandle, outAcquireFence, displayArea,
+            &testLayers, &clientLayers, &clearLayers);
+}
+
+
+Hwc2TestClientTargetSupport::Hwc2TestClientTargetSupport(
+        Hwc2TestCoverage coverage, const Area& displayArea)
+    : mBufferArea(coverage, displayArea),
+      mDataspace(coverage),
+      mSurfaceDamage(coverage)
+{
+    mBufferArea.setDependent(&mSurfaceDamage);
+}
+
+std::string Hwc2TestClientTargetSupport::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "client target: \n";
+
+    for (auto property : properties) {
+        dmp << property->dump();
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestClientTargetSupport::reset()
+{
+    for (auto property : properties) {
+        property->reset();
+    }
+}
+
+bool Hwc2TestClientTargetSupport::advance()
+{
+    for (auto property : properties) {
+        if (property->advance())
+            return true;
+    }
+    return false;
+}
+
+Area Hwc2TestClientTargetSupport::getBufferArea() const
+{
+    return mBufferArea.get();
+}
+
+android_dataspace_t Hwc2TestClientTargetSupport::getDataspace() const
+{
+    return mDataspace.get();
+}
+
+const hwc_region_t Hwc2TestClientTargetSupport::getSurfaceDamage() const
+{
+    return mSurfaceDamage.get();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
new file mode 100644
index 0000000..3b47978
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_CLIENT_TARGET_H
+#define _HWC2_TEST_CLIENT_TARGET_H
+
+#include <set>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestProperties.h"
+#include "Hwc2TestLayers.h"
+
+/* Generates client target buffers from client composition layers */
+class Hwc2TestClientTarget {
+public:
+    int getBuffer(const Hwc2TestLayers& layers,
+            const std::set<hwc2_layer_t>& clientLayers,
+            const std::set<hwc2_layer_t>& clearLayers,
+            bool clearClientTarget, const Area& displayArea,
+            buffer_handle_t* outHandle, int32_t* outAcquireFence);
+
+private:
+    Hwc2TestClientTargetBuffer mBuffer;
+};
+
+/* Generates valid client targets to test which ones the device will support */
+class Hwc2TestClientTargetSupport {
+public:
+    Hwc2TestClientTargetSupport(Hwc2TestCoverage coverage,
+            const Area& displayArea);
+
+    std::string dump() const;
+
+    void reset();
+    bool advance();
+
+    Area getBufferArea() const;
+    android_dataspace_t getDataspace() const;
+    const hwc_region_t getSurfaceDamage() const;
+
+private:
+    std::array<Hwc2TestContainer*, 3> properties = {{
+        &mDataspace, &mSurfaceDamage, &mBufferArea
+    }};
+
+    Hwc2TestBufferArea mBufferArea;
+    Hwc2TestDataspace mDataspace;
+    Hwc2TestSurfaceDamage mSurfaceDamage;
+};
+
+#endif /* ifndef _HWC2_TEST_CLIENT_TARGET_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
new file mode 100644
index 0000000..937fce2
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+
+#include "Hwc2TestLayer.h"
+
+Hwc2TestCoverage getCoverage(Hwc2TestPropertyName property,
+        Hwc2TestCoverage coverage, const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions) {
+    auto exception = coverageExceptions.find(property);
+    return (exception != coverageExceptions.end())? exception->second : coverage;
+}
+
+Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestLayer(coverage, displayArea,
+            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
+
+Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
+        const Area& displayArea, const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions)
+    : mBlendMode(getCoverage(Hwc2TestPropertyName::BlendMode, coverage,
+           coverageExceptions)),
+      mBufferArea(getCoverage(Hwc2TestPropertyName::BufferArea, coverage,
+           coverageExceptions), displayArea),
+      mColor(getCoverage(Hwc2TestPropertyName::Color, coverage,
+           coverageExceptions)),
+      mComposition(getCoverage(Hwc2TestPropertyName::Composition, coverage,
+           coverageExceptions)),
+      mDataspace(getCoverage(Hwc2TestPropertyName::Dataspace, coverage,
+           coverageExceptions)),
+      mDisplayFrame(getCoverage(Hwc2TestPropertyName::DisplayFrame, coverage,
+           coverageExceptions), displayArea),
+      mPlaneAlpha(getCoverage(Hwc2TestPropertyName::PlaneAlpha, coverage,
+           coverageExceptions)),
+      mSourceCrop(getCoverage(Hwc2TestPropertyName::SourceCrop, coverage,
+           coverageExceptions)),
+      mSurfaceDamage(getCoverage(Hwc2TestPropertyName::SurfaceDamage, coverage,
+           coverageExceptions)),
+      mTransform(getCoverage(Hwc2TestPropertyName::Transform, coverage,
+           coverageExceptions))
+{
+    mBufferArea.setDependent(&mBuffer);
+    mBufferArea.setDependent(&mSourceCrop);
+    mBufferArea.setDependent(&mSurfaceDamage);
+    mBlendMode.setDependent(&mColor);
+}
+
+std::string Hwc2TestLayer::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "layer: \n";
+
+    for (auto property : mProperties) {
+        dmp << property->dump();
+    }
+
+    dmp << mVisibleRegion.dump();
+    dmp << "\tz order: " << mZOrder << "\n";
+
+    return dmp.str();
+}
+
+int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
+        android::base::unique_fd* outAcquireFence)
+{
+    int32_t acquireFence;
+    int ret = mBuffer.get(outHandle, &acquireFence);
+    outAcquireFence->reset(acquireFence);
+    return ret;
+}
+
+int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    return mBuffer.get(outHandle, outAcquireFence);
+}
+
+void Hwc2TestLayer::setZOrder(uint32_t zOrder)
+{
+    mZOrder = zOrder;
+}
+
+void Hwc2TestLayer::setVisibleRegion(const android::Region& region)
+{
+    return mVisibleRegion.set(region);
+}
+
+void Hwc2TestLayer::reset()
+{
+    mVisibleRegion.release();
+
+    for (auto property : mProperties) {
+        property->reset();
+    }
+}
+
+bool Hwc2TestLayer::advance()
+{
+    for (auto property : mProperties) {
+        if (property->isSupported(mComposition.get()))
+            if (property->advance())
+                return true;
+    }
+    return false;
+}
+
+hwc2_blend_mode_t Hwc2TestLayer::getBlendMode() const
+{
+    return mBlendMode.get();
+}
+
+Area Hwc2TestLayer::getBufferArea() const
+{
+    return mBufferArea.get();
+}
+
+hwc_color_t Hwc2TestLayer::getColor() const
+{
+    return mColor.get();
+}
+
+hwc2_composition_t Hwc2TestLayer::getComposition() const
+{
+    return mComposition.get();
+}
+
+/* The cursor position corresponds to {displayFrame.left, displayFrame.top} */
+hwc_rect_t Hwc2TestLayer::getCursorPosition() const
+{
+    return mDisplayFrame.get();
+}
+
+android_dataspace_t Hwc2TestLayer::getDataspace() const
+{
+    return mDataspace.get();
+}
+
+hwc_rect_t Hwc2TestLayer::getDisplayFrame() const
+{
+    return mDisplayFrame.get();
+}
+
+float Hwc2TestLayer::getPlaneAlpha() const
+{
+    return mPlaneAlpha.get();
+}
+
+hwc_frect_t Hwc2TestLayer::getSourceCrop() const
+{
+    return mSourceCrop.get();
+}
+
+hwc_region_t Hwc2TestLayer::getSurfaceDamage() const
+{
+    return mSurfaceDamage.get();
+}
+
+hwc_transform_t Hwc2TestLayer::getTransform() const
+{
+    return mTransform.get();
+}
+
+hwc_region_t Hwc2TestLayer::getVisibleRegion() const
+{
+    return mVisibleRegion.get();
+}
+
+uint32_t Hwc2TestLayer::getZOrder() const
+{
+    return mZOrder;
+}
+
+bool Hwc2TestLayer::advanceBlendMode()
+{
+    return mBlendMode.advance();
+}
+
+bool Hwc2TestLayer::advanceBufferArea()
+{
+    return mBufferArea.advance();
+}
+
+bool Hwc2TestLayer::advanceColor()
+{
+    return mColor.advance();
+}
+
+bool Hwc2TestLayer::advanceComposition()
+{
+    return mComposition.advance();
+}
+
+bool Hwc2TestLayer::advanceCursorPosition()
+{
+    return mDisplayFrame.advance();
+}
+
+bool Hwc2TestLayer::advanceDataspace()
+{
+    return mDataspace.advance();
+}
+
+bool Hwc2TestLayer::advanceDisplayFrame()
+{
+    return mDisplayFrame.advance();
+}
+
+bool Hwc2TestLayer::advancePlaneAlpha()
+{
+    return mPlaneAlpha.advance();
+}
+
+bool Hwc2TestLayer::advanceSourceCrop()
+{
+    return mSourceCrop.advance();
+}
+
+bool Hwc2TestLayer::advanceSurfaceDamage()
+{
+    return mSurfaceDamage.advance();
+}
+
+bool Hwc2TestLayer::advanceTransform()
+{
+    return mTransform.advance();
+}
+
+bool Hwc2TestLayer::advanceVisibleRegion()
+{
+    if (mPlaneAlpha.advance())
+        return true;
+    return mDisplayFrame.advance();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
new file mode 100644
index 0000000..0e7dd22
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_LAYER_H
+#define _HWC2_TEST_LAYER_H
+
+#include <android-base/unique_fd.h>
+#include <unordered_map>
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+class Hwc2TestLayer {
+public:
+    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea,
+            const std::unordered_map<Hwc2TestPropertyName,
+            Hwc2TestCoverage>& coverage_exceptions);
+
+    std::string dump() const;
+
+    int getBuffer(buffer_handle_t* outHandle,
+            android::base::unique_fd* outAcquireFence);
+    int getBuffer(buffer_handle_t* outHandle, int32_t* outAcquireFence);
+
+    void setZOrder(uint32_t zOrder);
+    void setVisibleRegion(const android::Region& region);
+
+    void reset();
+    bool advance();
+
+    hwc2_blend_mode_t      getBlendMode() const;
+    Area                   getBufferArea() const;
+    hwc_color_t            getColor() const;
+    hwc2_composition_t     getComposition() const;
+    hwc_rect_t             getCursorPosition() const;
+    android_dataspace_t    getDataspace() const;
+    hwc_rect_t             getDisplayFrame() const;
+    float                  getPlaneAlpha() const;
+    hwc_frect_t            getSourceCrop() const;
+    hwc_region_t           getSurfaceDamage() const;
+    hwc_transform_t        getTransform() const;
+    hwc_region_t           getVisibleRegion() const;
+    uint32_t               getZOrder() const;
+
+    bool advanceBlendMode();
+    bool advanceBufferArea();
+    bool advanceColor();
+    bool advanceComposition();
+    bool advanceCursorPosition();
+    bool advanceDataspace();
+    bool advanceDisplayFrame();
+    bool advancePlaneAlpha();
+    bool advanceSourceCrop();
+    bool advanceSurfaceDamage();
+    bool advanceTransform();
+    bool advanceVisibleRegion();
+
+private:
+    std::array<Hwc2TestContainer*, 10> mProperties = {{
+        &mTransform, &mColor, &mDataspace, &mPlaneAlpha, &mSourceCrop,
+        &mSurfaceDamage, &mBlendMode, &mBufferArea, &mDisplayFrame,
+        &mComposition
+    }};
+
+    Hwc2TestBuffer mBuffer;
+
+    Hwc2TestBlendMode mBlendMode;
+    Hwc2TestBufferArea mBufferArea;
+    Hwc2TestColor mColor;
+    Hwc2TestComposition mComposition;
+    Hwc2TestDataspace mDataspace;
+    Hwc2TestDisplayFrame mDisplayFrame;
+    Hwc2TestPlaneAlpha mPlaneAlpha;
+    Hwc2TestSourceCrop mSourceCrop;
+    Hwc2TestSurfaceDamage mSurfaceDamage;
+    Hwc2TestTransform mTransform;
+    Hwc2TestVisibleRegion mVisibleRegion;
+
+    uint32_t mZOrder = UINT32_MAX;
+};
+
+#endif /* ifndef _HWC2_TEST_LAYER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
new file mode 100644
index 0000000..495ef79
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
@@ -0,0 +1,281 @@
+/* * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+#include "Hwc2TestLayers.h"
+
+Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+        Hwc2TestCoverage coverage, const Area& displayArea)
+    : Hwc2TestLayers(layers, coverage, displayArea,
+            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
+
+Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+        Hwc2TestCoverage coverage, const Area& displayArea,
+        const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions)
+    : mDisplayArea(displayArea)
+{
+    for (auto layer : layers) {
+        mTestLayers.emplace(std::piecewise_construct,
+                std::forward_as_tuple(layer),
+                std::forward_as_tuple(coverage, displayArea, coverageExceptions));
+    }
+
+    /* Iterate over the layers in order and assign z orders in the same order.
+     * This allows us to iterate over z orders in the same way when computing
+     * visible regions */
+    uint32_t nextZOrder = layers.size();
+
+    for (auto& testLayer : mTestLayers) {
+        testLayer.second.setZOrder(nextZOrder--);
+    }
+
+    setVisibleRegions();
+}
+
+std::string Hwc2TestLayers::dump() const
+{
+    std::stringstream dmp;
+    for (auto& testLayer : mTestLayers) {
+        dmp << testLayer.second.dump();
+    }
+    return dmp.str();
+}
+
+void Hwc2TestLayers::reset()
+{
+    for (auto& testLayer : mTestLayers) {
+        testLayer.second.reset();
+    }
+
+    setVisibleRegions();
+}
+
+bool Hwc2TestLayers::advance()
+{
+    auto itr = mTestLayers.begin();
+    bool optimized;
+
+    while (itr != mTestLayers.end()) {
+        if (itr->second.advance()) {
+            optimized = setVisibleRegions();
+            if (!mOptimize || optimized)
+                return true;
+            itr = mTestLayers.begin();
+        } else {
+            itr->second.reset();
+            ++itr;
+        }
+    }
+    return false;
+}
+
+bool Hwc2TestLayers::advanceVisibleRegions()
+{
+    auto itr = mTestLayers.begin();
+    bool optimized;
+
+    while (itr != mTestLayers.end()) {
+        if (itr->second.advanceVisibleRegion()) {
+            optimized = setVisibleRegions();
+            if (!mOptimize || optimized)
+                return true;
+            itr = mTestLayers.begin();
+        } else {
+            itr->second.reset();
+            ++itr;
+        }
+    }
+    return false;
+}
+
+/* Removes layouts that do not cover the entire display.
+ * Also removes layouts where a layer is completely blocked from view.
+ */
+bool Hwc2TestLayers::optimizeLayouts()
+{
+    mOptimize = true;
+
+    if (setVisibleRegions())
+        return true;
+    return advance();
+}
+
+bool Hwc2TestLayers::contains(hwc2_layer_t layer) const
+{
+    return mTestLayers.count(layer) != 0;
+}
+
+int Hwc2TestLayers::getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getBuffer(outHandle, outAcquireFence);
+}
+
+hwc2_blend_mode_t Hwc2TestLayers::getBlendMode(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getBlendMode();
+}
+
+Area Hwc2TestLayers::getBufferArea(hwc2_layer_t layer) const
+{
+    auto testLayer = mTestLayers.find(layer);
+    if (testLayer == mTestLayers.end())
+        [] () { GTEST_FAIL(); }();
+    return testLayer->second.getBufferArea();
+}
+
+hwc_color_t Hwc2TestLayers::getColor(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getColor();
+}
+
+hwc2_composition_t Hwc2TestLayers::getComposition(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getComposition();
+}
+
+hwc_rect_t Hwc2TestLayers::getCursorPosition(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getCursorPosition();
+}
+
+android_dataspace_t Hwc2TestLayers::getDataspace(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getDataspace();
+}
+
+hwc_rect_t Hwc2TestLayers::getDisplayFrame(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getDisplayFrame();
+}
+
+float Hwc2TestLayers::getPlaneAlpha(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getPlaneAlpha();
+}
+
+hwc_frect_t Hwc2TestLayers::getSourceCrop(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getSourceCrop();
+}
+
+hwc_region_t Hwc2TestLayers::getSurfaceDamage(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getSurfaceDamage();
+}
+
+hwc_transform_t Hwc2TestLayers::getTransform(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getTransform();
+}
+
+hwc_region_t Hwc2TestLayers::getVisibleRegion(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getVisibleRegion();
+}
+
+uint32_t Hwc2TestLayers::getZOrder(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getZOrder();
+}
+
+/* Sets the visible regions for a display. Returns false if the layers do not
+ * cover the entire display or if a layer is not visible */
+bool Hwc2TestLayers::setVisibleRegions()
+{
+    /* The region of the display that is covered by layers above the current
+     * layer */
+    android::Region aboveOpaqueLayers;
+
+    bool optimized = true;
+
+    /* Iterate over test layers from max z order to min z order. */
+    for (auto& testLayer : mTestLayers) {
+        android::Region visibleRegion;
+
+        /* Set the visible region of this layer */
+        const hwc_rect_t displayFrame = testLayer.second.getDisplayFrame();
+
+        visibleRegion.set(android::Rect(displayFrame.left, displayFrame.top,
+                displayFrame.right, displayFrame.bottom));
+
+        /* Remove the area covered by opaque layers above this layer
+         * from this layer's visible region */
+        visibleRegion.subtractSelf(aboveOpaqueLayers);
+
+        testLayer.second.setVisibleRegion(visibleRegion);
+
+        /* If a layer is not visible, return false */
+        if (visibleRegion.isEmpty())
+            optimized = false;
+
+        /* If this layer is opaque, store the region it covers */
+        if (testLayer.second.getPlaneAlpha() == 1.0f)
+            aboveOpaqueLayers.orSelf(visibleRegion);
+    }
+
+    /* If the opaque region does not cover the entire display return false */
+    if (!aboveOpaqueLayers.isRect())
+        return false;
+
+    const auto rect = aboveOpaqueLayers.begin();
+    if (rect->left != 0 || rect->top != 0 || rect->right != mDisplayArea.width
+            || rect->bottom != mDisplayArea.height)
+        return false;
+
+    return optimized;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
new file mode 100644
index 0000000..d95a91f
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_LAYERS_H
+#define _HWC2_TEST_LAYERS_H
+
+#include <map>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestProperties.h"
+#include "Hwc2TestLayer.h"
+
+class Hwc2TestLayers {
+public:
+    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestCoverage coverage, const Area& displayArea);
+
+    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestCoverage coverage, const Area& displayArea,
+            const std::unordered_map<Hwc2TestPropertyName,
+            Hwc2TestCoverage>& coverageExceptions);
+
+    std::string dump() const;
+
+    void reset();
+
+    bool advance();
+    bool advanceVisibleRegions();
+
+    /* Test cases with multiple layers and property values can take quite some
+     * time to run. A significant amount of time can be spent on test cases
+     * where one layer is changing property values but is not visible. To
+     * decrease runtime, this function can be called. Removes layouts where a
+     * layer is completely blocked from view. It also removes layouts that do
+     * not cover the entire display.*/
+    bool optimizeLayouts();
+
+    bool contains(hwc2_layer_t layer) const;
+
+    int  getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
+            int32_t* outAcquireFence);
+
+    hwc2_blend_mode_t      getBlendMode(hwc2_layer_t layer) const;
+    Area                   getBufferArea(hwc2_layer_t layer) const;
+    hwc_color_t            getColor(hwc2_layer_t layer) const;
+    hwc2_composition_t     getComposition(hwc2_layer_t layer) const;
+    hwc_rect_t             getCursorPosition(hwc2_layer_t layer) const;
+    android_dataspace_t    getDataspace(hwc2_layer_t layer) const;
+    hwc_rect_t             getDisplayFrame(hwc2_layer_t layer) const;
+    android_pixel_format_t getFormat(hwc2_layer_t layer) const;
+    float                  getPlaneAlpha(hwc2_layer_t layer) const;
+    hwc_frect_t            getSourceCrop(hwc2_layer_t layer) const;
+    hwc_region_t           getSurfaceDamage(hwc2_layer_t layer) const;
+    hwc_transform_t        getTransform(hwc2_layer_t layer) const;
+    hwc_region_t           getVisibleRegion(hwc2_layer_t layer) const;
+    uint32_t               getZOrder(hwc2_layer_t layer) const;
+
+private:
+    bool setVisibleRegions();
+
+    std::map<hwc2_layer_t, Hwc2TestLayer> mTestLayers;
+
+    Area mDisplayArea;
+
+    bool mOptimize = false;
+};
+
+#endif /* ifndef _HWC2_TEST_LAYERS_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
new file mode 100644
index 0000000..b5522de
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <cutils/log.h>
+#include <ui/Rect.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+Hwc2TestBufferArea::Hwc2TestBufferArea(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestProperty(mBufferAreas, mCompositionSupport),
+      mScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicScalars:
+            mDefaultScalars),
+      mDisplayArea(displayArea)
+{
+    update();
+}
+
+std::string Hwc2TestBufferArea::dump() const
+{
+    std::stringstream dmp;
+    const Area& curr = get();
+    dmp << "\tbuffer area: width " << curr.width << ", height " << curr.height
+            << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestBuffer* buffer)
+{
+    mBuffer = buffer;
+    if (buffer) {
+        buffer->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestSourceCrop* sourceCrop)
+{
+    mSourceCrop = sourceCrop;
+    if (mSourceCrop) {
+        mSourceCrop->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestSurfaceDamage* surfaceDamage)
+{
+    mSurfaceDamage = surfaceDamage;
+    if (mSurfaceDamage) {
+        mSurfaceDamage->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::update()
+{
+    mBufferAreas.clear();
+
+    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
+        mBufferAreas.push_back({0, 0});
+        return;
+    }
+
+    for (auto scalar : mScalars) {
+        mBufferAreas.push_back({static_cast<int32_t>(scalar * mDisplayArea.width),
+                static_cast<int32_t>(scalar * mDisplayArea.height)});
+    }
+
+    updateDependents();
+}
+
+void Hwc2TestBufferArea::updateDependents()
+{
+    const Area& curr = get();
+
+    if (mBuffer)
+        mBuffer->updateBufferArea(curr);
+    if (mSourceCrop)
+        mSourceCrop->updateBufferArea(curr);
+    if (mSurfaceDamage)
+        mSurfaceDamage->updateBufferArea(curr);
+}
+
+const std::vector<float> Hwc2TestBufferArea::mDefaultScalars = {
+    1.0f,
+};
+
+const std::vector<float> Hwc2TestBufferArea::mBasicScalars = {
+    1.0f, 0.5f,
+};
+
+const std::vector<float> Hwc2TestBufferArea::mCompleteScalars = {
+    1.0f, 0.75f, 0.5f
+};
+
+
+Hwc2TestBlendMode::Hwc2TestBlendMode(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteBlendModes, mBasicBlendModes,
+            mDefaultBlendModes, mCompositionSupport) { }
+
+std::string Hwc2TestBlendMode::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tblend mode: " << getBlendModeName(get()) << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestBlendMode::setDependent(Hwc2TestColor* color)
+{
+    mColor = color;
+    updateDependents();
+}
+
+void Hwc2TestBlendMode::updateDependents()
+{
+    if (mColor)
+        mColor->updateBlendMode(get());
+}
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mDefaultBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+};
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mBasicBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+    HWC2_BLEND_MODE_PREMULTIPLIED,
+};
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mCompleteBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+    HWC2_BLEND_MODE_PREMULTIPLIED,
+    HWC2_BLEND_MODE_COVERAGE,
+};
+
+
+Hwc2TestColor::Hwc2TestColor(Hwc2TestCoverage coverage,
+        hwc2_blend_mode_t blendMode)
+    : Hwc2TestProperty(mColors, mCompositionSupport),
+      mBaseColors((coverage == Hwc2TestCoverage::Complete)? mCompleteBaseColors:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicBaseColors:
+            mDefaultBaseColors),
+      mBlendMode(blendMode)
+{
+    update();
+}
+
+std::string Hwc2TestColor::dump() const
+{
+    std::stringstream dmp;
+    const hwc_color_t& color = get();
+    dmp << "\tcolor: r " << std::to_string(color.r) << ", g "
+            << std::to_string(color.g) << ", b " << std::to_string(color.b)
+            << ", a " << std::to_string(color.a) << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestColor::updateBlendMode(hwc2_blend_mode_t blendMode)
+{
+    mBlendMode = blendMode;
+    update();
+}
+
+void Hwc2TestColor::update()
+{
+    if (mBlendMode != HWC2_BLEND_MODE_PREMULTIPLIED) {
+        mColors = mBaseColors;
+        return;
+    }
+
+    mColors.clear();
+
+    for (const hwc_color_t& baseColor : mBaseColors) {
+        if (baseColor.a >= baseColor.r && baseColor.a >= baseColor.g
+                && baseColor.a >= baseColor.b) {
+            mColors.push_back(baseColor);
+        }
+    }
+
+}
+
+const std::vector<hwc_color_t> Hwc2TestColor::mDefaultBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+};
+
+const std::vector<hwc_color_t> Hwc2TestColor::mBasicBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {        0,         0,         0,         0},
+};
+
+const std::vector<hwc_color_t> Hwc2TestColor::mCompleteBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX,         0},
+    {UINT8_MAX, UINT8_MAX,         0, UINT8_MAX},
+    {UINT8_MAX, UINT8_MAX,         0,         0},
+    {UINT8_MAX,         0, UINT8_MAX, UINT8_MAX},
+    {UINT8_MAX,         0, UINT8_MAX,         0},
+    {UINT8_MAX,         0,         0, UINT8_MAX},
+    {UINT8_MAX,         0,         0,         0},
+    {        0, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {        0, UINT8_MAX, UINT8_MAX,         0},
+    {        0, UINT8_MAX,         0, UINT8_MAX},
+    {        0, UINT8_MAX,         0,         0},
+    {        0,         0, UINT8_MAX, UINT8_MAX},
+    {        0,         0, UINT8_MAX,         0},
+    {        0,         0,         0, UINT8_MAX},
+    {        0,         0,         0,         0},
+};
+
+
+Hwc2TestComposition::Hwc2TestComposition(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteCompositions, mBasicCompositions,
+            mDefaultCompositions, mCompositionSupport) { }
+
+std::string Hwc2TestComposition::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tcomposition: " << getCompositionName(get()) << "\n";
+    return dmp.str();
+}
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mDefaultCompositions = {
+    HWC2_COMPOSITION_DEVICE,
+};
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mBasicCompositions = {
+    HWC2_COMPOSITION_CLIENT,
+    HWC2_COMPOSITION_DEVICE,
+};
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mCompleteCompositions = {
+    HWC2_COMPOSITION_CLIENT,
+    HWC2_COMPOSITION_DEVICE,
+    HWC2_COMPOSITION_SOLID_COLOR,
+    HWC2_COMPOSITION_CURSOR,
+    HWC2_COMPOSITION_SIDEBAND,
+};
+
+
+Hwc2TestDataspace::Hwc2TestDataspace(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, completeDataspaces, basicDataspaces,
+            defaultDataspaces, mCompositionSupport) { }
+
+std::string Hwc2TestDataspace::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tdataspace: " << get() << "\n";
+    return dmp.str();
+}
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::defaultDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+};
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::basicDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+    HAL_DATASPACE_V0_SRGB,
+};
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::completeDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+    HAL_DATASPACE_ARBITRARY,
+    HAL_DATASPACE_STANDARD_SHIFT,
+    HAL_DATASPACE_STANDARD_MASK,
+    HAL_DATASPACE_STANDARD_UNSPECIFIED,
+    HAL_DATASPACE_STANDARD_BT709,
+    HAL_DATASPACE_STANDARD_BT601_625,
+    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED,
+    HAL_DATASPACE_STANDARD_BT601_525,
+    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED,
+    HAL_DATASPACE_STANDARD_BT2020,
+    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE,
+    HAL_DATASPACE_STANDARD_BT470M,
+    HAL_DATASPACE_STANDARD_FILM,
+    HAL_DATASPACE_TRANSFER_SHIFT,
+    HAL_DATASPACE_TRANSFER_MASK,
+    HAL_DATASPACE_TRANSFER_UNSPECIFIED,
+    HAL_DATASPACE_TRANSFER_LINEAR,
+    HAL_DATASPACE_TRANSFER_SRGB,
+    HAL_DATASPACE_TRANSFER_SMPTE_170M,
+    HAL_DATASPACE_TRANSFER_GAMMA2_2,
+    HAL_DATASPACE_TRANSFER_GAMMA2_8,
+    HAL_DATASPACE_TRANSFER_ST2084,
+    HAL_DATASPACE_TRANSFER_HLG,
+    HAL_DATASPACE_RANGE_SHIFT,
+    HAL_DATASPACE_RANGE_MASK,
+    HAL_DATASPACE_RANGE_UNSPECIFIED,
+    HAL_DATASPACE_RANGE_FULL,
+    HAL_DATASPACE_RANGE_LIMITED,
+    HAL_DATASPACE_SRGB_LINEAR,
+    HAL_DATASPACE_V0_SRGB_LINEAR,
+    HAL_DATASPACE_SRGB,
+    HAL_DATASPACE_V0_SRGB,
+    HAL_DATASPACE_JFIF,
+    HAL_DATASPACE_V0_JFIF,
+    HAL_DATASPACE_BT601_625,
+    HAL_DATASPACE_V0_BT601_625,
+    HAL_DATASPACE_BT601_525,
+    HAL_DATASPACE_V0_BT601_525,
+    HAL_DATASPACE_BT709,
+    HAL_DATASPACE_V0_BT709,
+    HAL_DATASPACE_DEPTH,
+};
+
+
+Hwc2TestDisplayDimension::Hwc2TestDisplayDimension(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(
+            (coverage == Hwc2TestCoverage::Complete)? mCompleteDisplayDimensions:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicDisplayDimensions:
+            mDefaultDisplayDimensions, mCompositionSupport) { }
+
+std::string Hwc2TestDisplayDimension::dump() const
+{
+    std::stringstream dmp;
+    const UnsignedArea& curr = get();
+    dmp << "\tdisplay dimension: " << curr.width<< " x " << curr.height<< "\n";
+    return dmp.str();
+}
+
+void Hwc2TestDisplayDimension::setDependent(Hwc2TestBuffer* buffer)
+{
+    mBuffer = buffer;
+    updateDependents();
+}
+
+void Hwc2TestDisplayDimension::updateDependents()
+{
+    const UnsignedArea& curr = get();
+
+    if (mBuffer)
+        mBuffer->updateBufferArea({static_cast<int32_t>(curr.width),
+                static_cast<int32_t>(curr.height)});
+}
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mDefaultDisplayDimensions = {
+    {1920, 1080},
+};
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mBasicDisplayDimensions = {
+    {640, 480},
+    {1280, 720},
+    {1920, 1080},
+    {1920, 1200},
+};
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mCompleteDisplayDimensions = {
+    {320, 240},
+    {480, 320},
+    {640, 480},
+    {1280, 720},
+    {1920, 1080},
+    {1920, 1200},
+    {2560, 1440},
+    {2560, 1600},
+    {3840, 2160},
+    {4096, 2160},
+};
+
+
+Hwc2TestDisplayFrame::Hwc2TestDisplayFrame(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestProperty(mDisplayFrames, mCompositionSupport),
+      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
+            mDefaultFrectScalars),
+      mDisplayArea(displayArea)
+{
+    update();
+}
+
+std::string Hwc2TestDisplayFrame::dump() const
+{
+    std::stringstream dmp;
+    const hwc_rect_t& displayFrame = get();
+    dmp << "\tdisplay frame: left " << displayFrame.left << ", top "
+            << displayFrame.top << ", right " << displayFrame.right
+            << ", bottom " << displayFrame.bottom << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestDisplayFrame::update()
+{
+    mDisplayFrames.clear();
+
+    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
+        mDisplayFrames.push_back({0, 0, 0, 0});
+        return;
+    }
+
+    for (const auto& frectScalar : mFrectScalars) {
+        mDisplayFrames.push_back({
+                static_cast<int>(frectScalar.left * mDisplayArea.width),
+                static_cast<int>(frectScalar.top * mDisplayArea.height),
+                static_cast<int>(frectScalar.right * mDisplayArea.width),
+                static_cast<int>(frectScalar.bottom * mDisplayArea.height)});
+    }
+}
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mDefaultFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mBasicFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 1.0, 0.05},
+    {0.0, 0.95, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mCompleteFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.05, 1.0, 0.95},
+    {0.0, 0.05, 1.0, 1.0},
+    {0.0, 0.0, 1.0, 0.05},
+    {0.0, 0.95, 1.0, 1.0},
+    {0.25, 0.0, 0.75, 0.35},
+    {0.25, 0.25, 0.75, 0.75},
+};
+
+
+Hwc2TestPlaneAlpha::Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompletePlaneAlphas, mBasicPlaneAlphas,
+            mDefaultPlaneAlphas, mCompositionSupport) { }
+
+std::string Hwc2TestPlaneAlpha::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tplane alpha: " << get() << "\n";
+    return dmp.str();
+}
+
+const std::vector<float> Hwc2TestPlaneAlpha::mDefaultPlaneAlphas = {
+    1.0f,
+};
+
+const std::vector<float> Hwc2TestPlaneAlpha::mBasicPlaneAlphas = {
+    1.0f, 0.0f,
+};
+
+const std::vector<float> Hwc2TestPlaneAlpha::mCompletePlaneAlphas = {
+    1.0f, 0.75f, 0.5f, 0.25f, 0.0f,
+};
+
+
+Hwc2TestSourceCrop::Hwc2TestSourceCrop(Hwc2TestCoverage coverage,
+        const Area& bufferArea)
+    : Hwc2TestProperty(mSourceCrops, mCompositionSupport),
+      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
+            mDefaultFrectScalars),
+      mBufferArea(bufferArea)
+{
+    update();
+}
+
+std::string Hwc2TestSourceCrop::dump() const
+{
+    std::stringstream dmp;
+    const hwc_frect_t& sourceCrop = get();
+    dmp << "\tsource crop: left " << sourceCrop.left << ", top "
+            << sourceCrop.top << ", right " << sourceCrop.right << ", bottom "
+            << sourceCrop.bottom << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestSourceCrop::updateBufferArea(const Area& bufferArea)
+{
+    mBufferArea = bufferArea;
+    update();
+}
+
+void Hwc2TestSourceCrop::update()
+{
+    mSourceCrops.clear();
+
+    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
+        mSourceCrops.push_back({0, 0, 0, 0});
+        return;
+    }
+
+    for (const auto& frectScalar : mFrectScalars) {
+        mSourceCrops.push_back({
+                frectScalar.left * mBufferArea.width,
+                frectScalar.top * mBufferArea.height,
+                frectScalar.right * mBufferArea.width,
+                frectScalar.bottom * mBufferArea.height});
+    }
+}
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mDefaultFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mBasicFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 0.5, 0.5},
+    {0.5, 0.5, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mCompleteFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 0.5, 0.5},
+    {0.5, 0.5, 1.0, 1.0},
+    {0.0, 0.0, 0.25, 0.25},
+    {0.25, 0.25, 0.75, 0.75},
+};
+
+
+Hwc2TestSurfaceDamage::Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(mSurfaceDamages, mCompositionSupport),
+      mRegionScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteRegionScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicRegionScalars:
+            mDefaultRegionScalars)
+{
+    update();
+}
+
+Hwc2TestSurfaceDamage::~Hwc2TestSurfaceDamage()
+{
+    freeSurfaceDamages();
+}
+
+std::string Hwc2TestSurfaceDamage::dump() const
+{
+    std::stringstream dmp;
+
+    const hwc_region_t& curr = get();
+    dmp << "\tsurface damage: region count " << curr.numRects << "\n";
+    for (size_t i = 0; i < curr.numRects; i++) {
+        const hwc_rect_t& rect = curr.rects[i];
+        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
+                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestSurfaceDamage::updateBufferArea(const Area& bufferArea)
+{
+    mBufferArea = bufferArea;
+    update();
+}
+
+void Hwc2TestSurfaceDamage::update()
+{
+    freeSurfaceDamages();
+
+    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
+        mSurfaceDamages.push_back({0, nullptr});
+        return;
+    }
+
+    hwc_region_t damage;
+
+    for (const auto& regionScalar : mRegionScalars) {
+        damage.numRects = regionScalar.size();
+
+        if (damage.numRects > 0) {
+            hwc_rect_t* rects = new hwc_rect_t[damage.numRects];
+            if (!rects) {
+                ALOGW("failed to allocate new hwc_rect_t array");
+                continue;
+            }
+
+            for (size_t i = 0; i < damage.numRects; i++) {
+                rects[i].left = regionScalar[i].left * mBufferArea.width;
+                rects[i].top = regionScalar[i].top * mBufferArea.height;
+                rects[i].right = regionScalar[i].right * mBufferArea.width;
+                rects[i].bottom = regionScalar[i].bottom * mBufferArea.height;
+            }
+
+            damage.rects = static_cast<hwc_rect_t const*>(rects);
+        } else {
+            damage.rects = nullptr;
+        }
+
+        mSurfaceDamages.push_back(damage);
+    }
+}
+
+void Hwc2TestSurfaceDamage::freeSurfaceDamages()
+{
+    for (const auto& surfaceDamage : mSurfaceDamages) {
+        if (surfaceDamage.numRects > 0 && surfaceDamage.rects)
+            delete[] surfaceDamage.rects;
+    }
+    mSurfaceDamages.clear();
+}
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mDefaultRegionScalars = {
+    {{}},
+};
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mBasicRegionScalars = {
+    {{}},
+    {{0.0, 0.0, 1.0, 1.0}},
+};
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mCompleteRegionScalars = {
+    {{}},
+    {{0.0, 0.0, 1.0, 1.0}},
+    {{0.0, 0.0, 0.5, 0.5}, {0.5, 0.5, 1.0, 1.0}},
+};
+
+
+Hwc2TestTransform::Hwc2TestTransform(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteTransforms, mBasicTransforms,
+            mDefaultTransforms, mCompositionSupport) { }
+
+std::string Hwc2TestTransform::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\ttransform: " << getTransformName(get()) << "\n";
+    return dmp.str();
+}
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mDefaultTransforms = {
+    static_cast<hwc_transform_t>(0),
+};
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mBasicTransforms = {
+    static_cast<hwc_transform_t>(0),
+    HWC_TRANSFORM_FLIP_H,
+    HWC_TRANSFORM_FLIP_V,
+    HWC_TRANSFORM_ROT_90,
+};
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mCompleteTransforms = {
+    static_cast<hwc_transform_t>(0),
+    HWC_TRANSFORM_FLIP_H,
+    HWC_TRANSFORM_FLIP_V,
+    HWC_TRANSFORM_ROT_90,
+    HWC_TRANSFORM_ROT_180,
+    HWC_TRANSFORM_ROT_270,
+    HWC_TRANSFORM_FLIP_H_ROT_90,
+    HWC_TRANSFORM_FLIP_V_ROT_90,
+};
+
+
+Hwc2TestVisibleRegion::~Hwc2TestVisibleRegion()
+{
+    release();
+}
+
+std::string Hwc2TestVisibleRegion::dump() const
+{
+    std::stringstream dmp;
+
+    const hwc_region_t& curr = get();
+    dmp << "\tvisible region: region count " << curr.numRects << "\n";
+    for (size_t i = 0; i < curr.numRects; i++) {
+        const hwc_rect_t& rect = curr.rects[i];
+        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
+                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestVisibleRegion::set(const android::Region& visibleRegion)
+{
+    release();
+
+    size_t size = 0;
+    const android::Rect* rects = visibleRegion.getArray(&size);
+
+    mVisibleRegion.numRects = size;
+    mVisibleRegion.rects = nullptr;
+
+    if (size > 0) {
+        hwc_rect_t* hwcRects = new hwc_rect_t[size];
+        for (size_t i = 0; i < size; i++) {
+            hwcRects[i].left = rects[i].left;
+            hwcRects[i].top = rects[i].top;
+            hwcRects[i].right = rects[i].right;
+            hwcRects[i].bottom = rects[i].bottom;
+        }
+        mVisibleRegion.rects = hwcRects;
+    }
+}
+
+hwc_region_t Hwc2TestVisibleRegion::get() const
+{
+    return mVisibleRegion;
+}
+
+void Hwc2TestVisibleRegion::release()
+{
+    if (mVisibleRegion.numRects > 0 && mVisibleRegion.rects)
+        delete[] mVisibleRegion.rects;
+    mVisibleRegion.rects = nullptr;
+    mVisibleRegion.numRects = 0;
+}
+
+/* Identifies which layer properties are supported by each composition type.
+ * hwc2_composition_t values range from:
+ *  HWC2_COMPOSITION_INVALID = 0,
+ *  HWC2_COMPOSITION_CLIENT = 1,
+ *  HWC2_COMPOSITION_DEVICE = 2,
+ *  HWC2_COMPOSITION_SOLID_COLOR = 3,
+ *  HWC2_COMPOSITION_CURSOR = 4,
+ *  HWC2_COMPOSITION_SIDEBAND = 5,
+ *
+ * Each property array can be indexed by a hwc2_composition_t value.
+ * By using an array instead of a more complex data structure, runtimes for
+ * some test cases showed a noticeable improvement.
+ */
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestBufferArea::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestBlendMode::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestColor::mCompositionSupport = {{
+    false,   false,   false,   true,    false,   false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestComposition::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDataspace::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDisplayDimension::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDisplayFrame::mCompositionSupport = {{
+    false,   true,    true,    true,    false,   true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestPlaneAlpha::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestSourceCrop::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestSurfaceDamage::mCompositionSupport = {{
+    false,   false,   true,    false,   true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestTransform::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
new file mode 100644
index 0000000..c2029ab
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_PROPERTIES_H
+#define _HWC2_TEST_PROPERTIES_H
+
+#include <array>
+#include <vector>
+
+#include <ui/Region.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+enum class Hwc2TestCoverage {
+    Default = 0,
+    Basic,
+    Complete,
+};
+
+enum class Hwc2TestPropertyName {
+    BlendMode = 1,
+    BufferArea,
+    Color,
+    Composition,
+    CursorPosition,
+    Dataspace,
+    DisplayFrame,
+    PlaneAlpha,
+    SourceCrop,
+    SurfaceDamage,
+    Transform,
+};
+
+typedef struct {
+    int32_t width;
+    int32_t height;
+} Area;
+
+
+typedef struct {
+    uint32_t width;
+    uint32_t height;
+} UnsignedArea;
+
+
+class Hwc2TestContainer {
+public:
+    virtual ~Hwc2TestContainer() = default;
+
+    /* Resets the container */
+    virtual void reset() = 0;
+
+    /* Attempts to advance to the next valid value. Returns true if one can be
+     * found */
+    virtual bool advance() = 0;
+
+    virtual std::string dump() const = 0;
+
+    /* Returns true if the container supports the given composition type */
+    virtual bool isSupported(hwc2_composition_t composition) = 0;
+};
+
+
+template <class T>
+class Hwc2TestProperty : public Hwc2TestContainer {
+public:
+    Hwc2TestProperty(Hwc2TestCoverage coverage,
+            const std::vector<T>& completeList, const std::vector<T>& basicList,
+            const std::vector<T>& defaultList,
+            const std::array<bool, 6>& compositionSupport)
+        : Hwc2TestProperty((coverage == Hwc2TestCoverage::Complete)? completeList:
+                (coverage == Hwc2TestCoverage::Basic)? basicList : defaultList,
+                compositionSupport) { }
+
+    Hwc2TestProperty(const std::vector<T>& list,
+            const std::array<bool, 6>& compositionSupport)
+        : mList(list),
+          mCompositionSupport(compositionSupport) { }
+
+    void reset() override
+    {
+        mListIdx = 0;
+    }
+
+    bool advance() override
+    {
+        if (mListIdx + 1 < mList.size()) {
+            mListIdx++;
+            updateDependents();
+            return true;
+        }
+        reset();
+        updateDependents();
+        return false;
+    }
+
+    T get() const
+    {
+        return mList.at(mListIdx);
+    }
+
+    virtual bool isSupported(hwc2_composition_t composition)
+    {
+        return mCompositionSupport.at(composition);
+    }
+
+protected:
+    /* If a derived class has dependents, override this function */
+    virtual void updateDependents() { }
+
+    const std::vector<T>& mList;
+    size_t mListIdx = 0;
+
+    const std::array<bool, 6>& mCompositionSupport;
+};
+
+class Hwc2TestBuffer;
+class Hwc2TestSourceCrop;
+class Hwc2TestSurfaceDamage;
+
+class Hwc2TestBufferArea : public Hwc2TestProperty<Area> {
+public:
+    Hwc2TestBufferArea(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    std::string dump() const override;
+
+    void setDependent(Hwc2TestBuffer* buffer);
+    void setDependent(Hwc2TestSourceCrop* sourceCrop);
+    void setDependent(Hwc2TestSurfaceDamage* surfaceDamage);
+
+protected:
+    void update();
+    void updateDependents() override;
+
+    const std::vector<float>& mScalars;
+    static const std::vector<float> mDefaultScalars;
+    static const std::vector<float> mBasicScalars;
+    static const std::vector<float> mCompleteScalars;
+
+    Area mDisplayArea;
+
+    Hwc2TestBuffer* mBuffer = nullptr;
+    Hwc2TestSourceCrop* mSourceCrop = nullptr;
+    Hwc2TestSurfaceDamage* mSurfaceDamage = nullptr;
+
+    std::vector<Area> mBufferAreas;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestColor;
+
+class Hwc2TestBlendMode : public Hwc2TestProperty<hwc2_blend_mode_t> {
+public:
+    Hwc2TestBlendMode(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+    void setDependent(Hwc2TestColor* color);
+
+protected:
+    void updateDependents() override;
+
+    Hwc2TestColor* mColor = nullptr;
+
+    static const std::vector<hwc2_blend_mode_t> mDefaultBlendModes;
+    static const std::vector<hwc2_blend_mode_t> mBasicBlendModes;
+    static const std::vector<hwc2_blend_mode_t> mCompleteBlendModes;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestColor : public Hwc2TestProperty<hwc_color_t> {
+public:
+    Hwc2TestColor(Hwc2TestCoverage coverage,
+            hwc2_blend_mode_t blendMode = HWC2_BLEND_MODE_NONE);
+
+    std::string dump() const override;
+
+    void updateBlendMode(hwc2_blend_mode_t blendMode);
+
+protected:
+    void update();
+
+    std::vector<hwc_color_t> mBaseColors;
+    static const std::vector<hwc_color_t> mDefaultBaseColors;
+    static const std::vector<hwc_color_t> mBasicBaseColors;
+    static const std::vector<hwc_color_t> mCompleteBaseColors;
+
+    hwc2_blend_mode_t mBlendMode;
+
+    std::vector<hwc_color_t> mColors;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestComposition : public Hwc2TestProperty<hwc2_composition_t> {
+public:
+    Hwc2TestComposition(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<hwc2_composition_t> mDefaultCompositions;
+    static const std::vector<hwc2_composition_t> mBasicCompositions;
+    static const std::vector<hwc2_composition_t> mCompleteCompositions;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDataspace : public Hwc2TestProperty<android_dataspace_t> {
+public:
+    Hwc2TestDataspace(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<android_dataspace_t> defaultDataspaces;
+    static const std::vector<android_dataspace_t> basicDataspaces;
+    static const std::vector<android_dataspace_t> completeDataspaces;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> {
+public:
+    Hwc2TestDisplayDimension(Hwc2TestCoverage coverage);
+
+    std::string dump() const;
+
+    void setDependent(Hwc2TestBuffer* buffer);
+
+private:
+    void updateDependents();
+
+    Hwc2TestBuffer* mBuffer;
+
+    static const std::vector<UnsignedArea> mDefaultDisplayDimensions;
+    static const std::vector<UnsignedArea> mBasicDisplayDimensions;
+    static const std::vector<UnsignedArea> mCompleteDisplayDimensions;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDisplayFrame : public Hwc2TestProperty<hwc_rect_t> {
+public:
+    Hwc2TestDisplayFrame(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    std::string dump() const override;
+
+protected:
+    void update();
+
+    const std::vector<hwc_frect_t>& mFrectScalars;
+    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
+    const static std::vector<hwc_frect_t> mBasicFrectScalars;
+    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
+
+    Area mDisplayArea;
+
+    std::vector<hwc_rect_t> mDisplayFrames;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestPlaneAlpha : public Hwc2TestProperty<float> {
+public:
+    Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<float> mDefaultPlaneAlphas;
+    static const std::vector<float> mBasicPlaneAlphas;
+    static const std::vector<float> mCompletePlaneAlphas;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestSourceCrop : public Hwc2TestProperty<hwc_frect_t> {
+public:
+    Hwc2TestSourceCrop(Hwc2TestCoverage coverage, const Area& bufferArea = {0, 0});
+
+    std::string dump() const override;
+
+    void updateBufferArea(const Area& bufferArea);
+
+protected:
+    void update();
+
+    const std::vector<hwc_frect_t>& mFrectScalars;
+    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
+    const static std::vector<hwc_frect_t> mBasicFrectScalars;
+    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
+
+    Area mBufferArea;
+
+    std::vector<hwc_frect_t> mSourceCrops;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestSurfaceDamage : public Hwc2TestProperty<hwc_region_t> {
+public:
+    Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage);
+    ~Hwc2TestSurfaceDamage();
+
+    std::string dump() const override;
+
+    void updateBufferArea(const Area& bufferArea);
+
+protected:
+    void update();
+    void freeSurfaceDamages();
+
+    const std::vector<std::vector<hwc_frect_t>> &mRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mDefaultRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mBasicRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mCompleteRegionScalars;
+
+    Area mBufferArea = {0, 0};
+
+    std::vector<hwc_region_t> mSurfaceDamages;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestTransform : public Hwc2TestProperty<hwc_transform_t> {
+public:
+    Hwc2TestTransform(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<hwc_transform_t> mDefaultTransforms;
+    static const std::vector<hwc_transform_t> mBasicTransforms;
+    static const std::vector<hwc_transform_t> mCompleteTransforms;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestVisibleRegion {
+public:
+    ~Hwc2TestVisibleRegion();
+
+    std::string dump() const;
+
+    void set(const android::Region& visibleRegion);
+    hwc_region_t get() const;
+    void release();
+
+protected:
+    hwc_region_t mVisibleRegion = {0, nullptr};
+};
+
+#endif /* ifndef _HWC2_TEST_PROPERTIES_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
new file mode 100644
index 0000000..d0fbc0b
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+
+#include "Hwc2TestVirtualDisplay.h"
+
+Hwc2TestVirtualDisplay::Hwc2TestVirtualDisplay(
+        Hwc2TestCoverage coverage)
+    : mDisplayDimension(coverage)
+{
+    mDisplayDimension.setDependent(&mBuffer);
+}
+
+std::string Hwc2TestVirtualDisplay::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "virtual display: \n";
+
+    mDisplayDimension.dump();
+
+    return dmp.str();
+}
+
+int Hwc2TestVirtualDisplay::getBuffer(buffer_handle_t* outHandle,
+        android::base::unique_fd* outAcquireFence)
+{
+    int32_t acquireFence;
+    int ret = mBuffer.get(outHandle, &acquireFence);
+    outAcquireFence->reset(acquireFence);
+    return ret;
+}
+
+void Hwc2TestVirtualDisplay::reset()
+{
+    return mDisplayDimension.reset();
+}
+
+bool Hwc2TestVirtualDisplay::advance()
+{
+    return mDisplayDimension.advance();
+}
+
+UnsignedArea Hwc2TestVirtualDisplay::getDisplayDimension() const
+{
+    return mDisplayDimension.get();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
new file mode 100644
index 0000000..09420ef
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _HWC2_TEST_VIRTUAL_DISPLAY_H
+#define _HWC2_TEST_VIRTUAL_DISPLAY_H
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+class Hwc2TestVirtualDisplay {
+public:
+    Hwc2TestVirtualDisplay(Hwc2TestCoverage coverage);
+
+    std::string dump() const;
+
+    int getBuffer(buffer_handle_t* outHandle,
+            android::base::unique_fd* outAcquireFence);
+
+    void reset();
+    bool advance();
+
+    UnsignedArea getDisplayDimension() const;
+
+private:
+    Hwc2TestBuffer mBuffer;
+
+    Hwc2TestDisplayDimension mDisplayDimension;
+};
+
+#endif /* ifndef _HWC2_TEST_VIRTUAL_DISPLAY_H */
diff --git a/services/vr/.clang-format b/services/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/services/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/services/vr/Android.bp b/services/vr/Android.bp
new file mode 100644
index 0000000..80df479
--- /dev/null
+++ b/services/vr/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+  "*",
+]
diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk
new file mode 100644
index 0000000..97f0332
--- /dev/null
+++ b/services/vr/bufferhubd/Android.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+    buffer_hub.cpp \
+    bufferhubd.cpp \
+    consumer_channel.cpp \
+    producer_channel.cpp \
+    consumer_queue_channel.cpp \
+    producer_queue_channel.cpp \
+
+staticLibraries := \
+	libperformance \
+	libpdx_default_transport \
+	libbufferhub
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libsync \
+	libutils \
+        libgui \
+        libui
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"bufferhubd\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhubd
+LOCAL_INIT_RC := bufferhubd.rc
+include $(BUILD_EXECUTABLE)
+
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
new file mode 100644
index 0000000..d27f274
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,489 @@
+#include "buffer_hub.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <poll.h>
+#include <utils/Trace.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+#include "producer_channel.h"
+#include "producer_queue_channel.h"
+
+using android::pdx::Channel;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+BufferHubService::BufferHubService()
+    : BASE("BufferHub", Endpoint::Create(BufferHubRPC::kClientPath)) {}
+
+BufferHubService::~BufferHubService() {}
+
+bool BufferHubService::IsInitialized() const { return BASE::IsInitialized(); }
+
+std::string BufferHubService::DumpState(size_t /*max_length*/) {
+  std::ostringstream stream;
+  auto channels = GetChannels<BufferHubChannel>();
+
+  std::sort(channels.begin(), channels.end(),
+            [](const std::shared_ptr<BufferHubChannel>& a,
+               const std::shared_ptr<BufferHubChannel>& b) {
+              return a->buffer_id() < b->buffer_id();
+            });
+
+  stream << "Active Producer Buffers:\n";
+  stream << std::right;
+  stream << std::setw(6) << "Id";
+  stream << " ";
+  stream << std::setw(9) << "Consumers";
+  stream << " ";
+  stream << std::setw(14) << "Geometry";
+  stream << " ";
+  stream << std::setw(6) << "Format";
+  stream << " ";
+  stream << std::setw(11) << "Usage";
+  stream << " ";
+  stream << "Name";
+  stream << std::endl;
+
+  for (const auto& channel : channels) {
+    if (channel->channel_type() == BufferHubChannel::kProducerType) {
+      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+      stream << std::right;
+      stream << std::setw(6) << info.id;
+      stream << " ";
+      stream << std::setw(9) << info.consumer_count;
+      stream << " ";
+      if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+        std::string size = std::to_string(info.width) + " B";
+        stream << std::setw(14) << size;
+      } else {
+        std::string dimensions = std::to_string(info.width) + "x" +
+                                 std::to_string(info.height) + "x" +
+                                 std::to_string(info.layer_count);
+        stream << std::setw(14) << dimensions;
+      }
+      stream << " ";
+      stream << std::setw(6) << info.format;
+      stream << " ";
+      stream << "0x" << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage;
+      stream << std::dec << std::setfill(' ');
+      stream << " ";
+      stream << info.name;
+      stream << std::endl;
+    }
+  }
+
+  stream << "Active Consumer Buffers:\n";
+  stream << std::right;
+  stream << std::setw(6) << "Id";
+  stream << " ";
+  stream << std::setw(14) << "Geometry";
+  stream << " ";
+  stream << "Name";
+  stream << std::endl;
+
+  for (const auto& channel : channels) {
+    if (channel->channel_type() == BufferHubChannel::kConsumerType) {
+      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+      stream << std::right;
+      stream << std::setw(6) << info.id;
+      stream << " ";
+
+      if (info.consumer_count == 0) {
+        // consumer_count is tracked by producer. When it's zero, producer must
+        // have already hung up and the consumer is orphaned.
+        stream << std::setw(14) << "Orphaned.";
+        stream << (" channel_id=" + std::to_string(channel->channel_id()));
+        stream << std::endl;
+        continue;
+      }
+
+      if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+        std::string size = std::to_string(info.width) + " B";
+        stream << std::setw(14) << size;
+      } else {
+        std::string dimensions = std::to_string(info.width) + "x" +
+                                 std::to_string(info.height) + "x" +
+                                 std::to_string(info.layer_count);
+        stream << std::setw(14) << dimensions;
+      }
+      stream << " ";
+      stream << info.name;
+      stream << std::endl;
+    }
+  }
+
+  stream << std::endl;
+  stream << "Active Producer Queues:\n";
+  stream << std::right << std::setw(6) << "Id";
+  stream << std::right << std::setw(12) << " Capacity";
+  stream << std::right << std::setw(12) << " Consumers";
+  stream << " UsageSetMask";
+  stream << " UsageClearMask";
+  stream << " UsageDenySetMask";
+  stream << " UsageDenyClearMask";
+  stream << " ";
+  stream << std::endl;
+
+  for (const auto& channel : channels) {
+    if (channel->channel_type() == BufferHubChannel::kProducerQueueType) {
+      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+      stream << std::dec << std::setfill(' ');
+      stream << std::right << std::setw(6) << info.id;
+      stream << std::right << std::setw(12) << info.capacity;
+      stream << std::right << std::setw(12) << info.consumer_count;
+      stream << std::setw(5) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.usage_set_mask;
+      stream << std::setw(7) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.usage_clear_mask;
+      stream << std::setw(9) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.usage_deny_set_mask;
+      stream << std::setw(11) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.usage_deny_clear_mask;
+      stream << std::hex << std::setfill('0');
+      stream << std::endl;
+    }
+  }
+
+  stream << std::endl;
+  stream << "Active Consumer Queues:\n";
+  stream << std::dec << std::setfill(' ');
+  stream << std::right << std::setw(6) << "Id";
+  stream << std::right << std::setw(12) << " Imported";
+  stream << " ";
+  stream << std::endl;
+
+  for (const auto& channel : channels) {
+    if (channel->channel_type() == BufferHubChannel::kConsumerQueueType) {
+      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+      stream << std::right << std::setw(6) << info.id;
+      stream << std::right << std::setw(12) << info.capacity;
+      stream << std::endl;
+    }
+  }
+
+  return stream.str();
+}
+
+void BufferHubService::HandleImpulse(Message& message) {
+  ATRACE_NAME("BufferHubService::HandleImpulse");
+  if (auto channel = message.GetChannel<BufferHubChannel>())
+    channel->HandleImpulse(message);
+}
+
+pdx::Status<void> BufferHubService::HandleMessage(Message& message) {
+  ATRACE_NAME("BufferHubService::HandleMessage");
+  auto channel = message.GetChannel<BufferHubChannel>();
+
+  ALOGD_IF(
+      TRACE,
+      "BufferHubService::HandleMessage: channel=%p channel_id=%d opcode=%d",
+      channel.get(), message.GetChannelId(), message.GetOp());
+
+  // If the channel is already set up, let it handle the message.
+  if (channel && !channel->HandleMessage(message))
+    return DefaultHandleMessage(message);
+
+  // This channel has not been set up yet, the following are valid operations.
+  switch (message.GetOp()) {
+    case BufferHubRPC::CreateBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreateBuffer>(
+          *this, &BufferHubService::OnCreateBuffer, message);
+      return {};
+
+    case BufferHubRPC::CreatePersistentBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+          *this, &BufferHubService::OnCreatePersistentBuffer, message);
+      return {};
+
+    case BufferHubRPC::GetPersistentBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetPersistentBuffer>(
+          *this, &BufferHubService::OnGetPersistentBuffer, message);
+      return {};
+
+    case BufferHubRPC::CreateProducerQueue::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+          *this, &BufferHubService::OnCreateProducerQueue, message);
+      return {};
+
+    default:
+      return DefaultHandleMessage(message);
+  }
+}
+
+void BufferHubService::OnChannelClose(Message&,
+                                      const std::shared_ptr<Channel>& channel) {
+  if (auto buffer = std::static_pointer_cast<BufferHubChannel>(channel))
+    buffer->Detach();
+}
+
+Status<void> BufferHubService::OnCreateBuffer(Message& message, uint32_t width,
+                                              uint32_t height, uint32_t format,
+                                              uint64_t usage,
+                                              size_t meta_size_bytes) {
+  // Use the producer channel id as the global buffer id.
+  const int buffer_id = message.GetChannelId();
+  ALOGD_IF(TRACE,
+           "BufferHubService::OnCreateBuffer: buffer_id=%d width=%u height=%u "
+           "format=%u usage=%" PRIx64 " meta_size_bytes=%zu",
+           buffer_id, width, height, format, usage, meta_size_bytes);
+
+  // See if this channel is already attached to a buffer.
+  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+    ALOGE("BufferHubService::OnCreateBuffer: Buffer already created: buffer=%d",
+          buffer_id);
+    return ErrorStatus(EALREADY);
+  }
+  const uint32_t kDefaultLayerCount = 1;
+  auto status = ProducerChannel::Create(this, buffer_id, width, height,
+                                        kDefaultLayerCount, format, usage,
+                                        meta_size_bytes);
+  if (status) {
+    message.SetChannel(status.take());
+    return {};
+  } else {
+    ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer: %s",
+          status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+}
+
+Status<void> BufferHubService::OnCreatePersistentBuffer(
+    Message& message, 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) {
+  const uint32_t kDefaultLayerCount = 1;
+  const int channel_id = message.GetChannelId();
+  ALOGD_IF(TRACE,
+           "BufferHubService::OnCreatePersistentBuffer: channel_id=%d name=%s "
+           "user_id=%d group_id=%d width=%u height=%u format=%u "
+           "usage=%" PRIx64 " meta_size_bytes=%zu",
+           channel_id, name.c_str(), user_id, group_id, width, height, format,
+           usage, meta_size_bytes);
+
+  // See if this channel is already attached to a buffer.
+  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+    ALOGE(
+        "BufferHubService::OnCreatePersistentBuffer: Channel already attached "
+        "to buffer: channel_id=%d buffer_id=%d",
+        channel_id, channel->buffer_id());
+    return ErrorStatus(EALREADY);
+  }
+
+  const int euid = message.GetEffectiveUserId();
+  const int egid = message.GetEffectiveGroupId();
+
+  if (auto buffer = GetNamedBuffer(name)) {
+    if (!buffer->CheckAccess(euid, egid)) {
+      ALOGE(
+          "BufferHubService::OnCreatePersistentBuffer: Requesting process does "
+          "not have permission to access named buffer: name=%s euid=%d egid=%d",
+          name.c_str(), euid, euid);
+      return ErrorStatus(EPERM);
+    } else if (!buffer->CheckParameters(width, height, kDefaultLayerCount,
+                                        format, usage, meta_size_bytes)) {
+      ALOGE(
+          "BufferHubService::OnCreatePersistentBuffer: Requested an existing "
+          "buffer with different parameters: name=%s",
+          name.c_str());
+      return ErrorStatus(EINVAL);
+    } else if (!buffer->IsDetached()) {
+      ALOGE(
+          "BufferHubService::OnCreatePersistentBuffer: Requesting a persistent "
+          "buffer that is already attached to a channel: name=%s",
+          name.c_str());
+      return ErrorStatus(EINVAL);
+    } else {
+      buffer->Attach(channel_id);
+      message.SetChannel(buffer);
+      return {};
+    }
+  } else {
+    auto status = ProducerChannel::Create(this, channel_id, width, height,
+                                          kDefaultLayerCount, format, usage,
+                                          meta_size_bytes);
+    if (!status) {
+      ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+      return status.error_status();
+    }
+    auto persistent_buffer = status.take();
+    auto make_persistent_status = persistent_buffer->OnProducerMakePersistent(
+        message, name, user_id, group_id);
+    if (make_persistent_status)
+      message.SetChannel(persistent_buffer);
+    return make_persistent_status;
+  }
+}
+
+Status<void> BufferHubService::OnGetPersistentBuffer(Message& message,
+                                                     const std::string& name) {
+  const int channel_id = message.GetChannelId();
+  ALOGD_IF(TRACE,
+           "BufferHubService::OnGetPersistentBuffer: channel_id=%d name=%s",
+           channel_id, name.c_str());
+
+  // See if this channel is already attached to a buffer.
+  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+    ALOGE(
+        "BufferHubService::OnGetPersistentBuffer: Channel already attached to "
+        "buffer: channel_id=%d buffer_id=%d",
+        channel_id, channel->buffer_id());
+    return ErrorStatus(EALREADY);
+  }
+
+  const int euid = message.GetEffectiveUserId();
+  const int egid = message.GetEffectiveGroupId();
+
+  if (auto buffer = GetNamedBuffer(name)) {
+    if (!buffer->CheckAccess(euid, egid)) {
+      ALOGE(
+          "BufferHubService::OnGetPersistentBuffer: Requesting process does "
+          "not have permission to access named buffer: name=%s euid=%d egid=%d",
+          name.c_str(), euid, egid);
+      return ErrorStatus(EPERM);
+    } else if (!buffer->IsDetached()) {
+      ALOGE(
+          "BufferHubService::OnGetPersistentBuffer: Requesting a persistent "
+          "buffer that is already attached to a channel: name=%s",
+          name.c_str());
+      return ErrorStatus(EINVAL);
+    } else {
+      buffer->Attach(channel_id);
+      message.SetChannel(buffer);
+      return {};
+    }
+  } else {
+    ALOGE("BufferHubService::OnGetPersistentBuffer: Buffer \"%s\" not found!",
+          name.c_str());
+    return ErrorStatus(ENOENT);
+  }
+}
+
+Status<QueueInfo> BufferHubService::OnCreateProducerQueue(
+    pdx::Message& message, size_t meta_size_bytes,
+    const UsagePolicy& usage_policy) {
+  // Use the producer channel id as the global queue id.
+  const int queue_id = message.GetChannelId();
+  ALOGD_IF(TRACE, "BufferHubService::OnCreateProducerQueue: queue_id=%d",
+           queue_id);
+
+  // See if this channel is already attached to another object.
+  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+    ALOGE("BufferHubService::OnCreateProducerQueue: already created: queue=%d",
+          queue_id);
+    return ErrorStatus(EALREADY);
+  }
+
+  auto status = ProducerQueueChannel::Create(this, queue_id, meta_size_bytes,
+                                             usage_policy);
+  if (status) {
+    message.SetChannel(status.take());
+    return {{meta_size_bytes, queue_id}};
+  } else {
+    ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+    return status.error_status();
+  }
+}
+
+bool BufferHubService::AddNamedBuffer(
+    const std::string& name, const std::shared_ptr<ProducerChannel>& buffer) {
+  auto search = named_buffers_.find(name);
+  if (search == named_buffers_.end()) {
+    named_buffers_.emplace(name, buffer);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+std::shared_ptr<ProducerChannel> BufferHubService::GetNamedBuffer(
+    const std::string& name) {
+  auto search = named_buffers_.find(name);
+  if (search != named_buffers_.end())
+    return search->second;
+  else
+    return nullptr;
+}
+
+bool BufferHubService::RemoveNamedBuffer(const ProducerChannel& buffer) {
+  for (auto it = named_buffers_.begin(); it != named_buffers_.end();) {
+    if (it->second.get() == &buffer) {
+      named_buffers_.erase(it);
+      return true;
+    }
+    ++it;
+  }
+  return false;
+}
+
+void BufferHubChannel::SignalAvailable() {
+  ATRACE_NAME("BufferHubChannel::SignalAvailable");
+  ALOGD_IF(TRACE,
+           "BufferHubChannel::SignalAvailable: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  if (!IsDetached()) {
+    const auto status = service_->ModifyChannelEvents(channel_id_, 0, POLLIN);
+    ALOGE_IF(!status,
+             "BufferHubChannel::SignalAvailable: failed to signal availability "
+             "channel_id=%d: %s",
+             channel_id_, status.GetErrorMessage().c_str());
+  } else {
+    ALOGD_IF(TRACE, "BufferHubChannel::SignalAvailable: detached buffer.");
+  }
+}
+
+void BufferHubChannel::ClearAvailable() {
+  ATRACE_NAME("BufferHubChannel::ClearAvailable");
+  ALOGD_IF(TRACE,
+           "BufferHubChannel::ClearAvailable: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  if (!IsDetached()) {
+    const auto status = service_->ModifyChannelEvents(channel_id_, POLLIN, 0);
+    ALOGE_IF(!status,
+             "BufferHubChannel::ClearAvailable: failed to clear availability "
+             "channel_id=%d: %s",
+             channel_id_, status.GetErrorMessage().c_str());
+  } else {
+    ALOGD_IF(TRACE, "BufferHubChannel::ClearAvailable: detached buffer.");
+  }
+}
+
+void BufferHubChannel::Hangup() {
+  ATRACE_NAME("BufferHubChannel::Hangup");
+  ALOGD_IF(TRACE, "BufferHubChannel::Hangup: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  if (!IsDetached()) {
+    const auto status = service_->ModifyChannelEvents(channel_id_, 0, POLLHUP);
+    ALOGE_IF(
+        !status,
+        "BufferHubChannel::Hangup: failed to signal hangup channel_id=%d: %s",
+        channel_id_, status.GetErrorMessage().c_str());
+  } else {
+    ALOGD_IF(TRACE, "BufferHubChannel::Hangup: detached buffer.");
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
new file mode 100644
index 0000000..3bc2635
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -0,0 +1,180 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+#define ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <hardware/gralloc.h>
+#include <pdx/service.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubService;
+class ConsumerChannel;
+class ProducerChannel;
+class ConsumerQueueChannel;
+class ProducerQueueChannel;
+
+class BufferHubChannel : public pdx::Channel {
+ public:
+  enum ChannelType {
+    kProducerType,
+    kConsumerType,
+    kProducerQueueType,
+    kConsumerQueueType,
+  };
+
+  enum : int { kDetachedId = -1 };
+
+  BufferHubChannel(BufferHubService* service, int buffer_id, int channel_id,
+                   ChannelType channel_type)
+      : service_(service),
+        buffer_id_(buffer_id),
+        channel_id_(channel_id),
+        channel_type_(channel_type) {}
+  virtual ~BufferHubChannel() {}
+
+  virtual bool HandleMessage(pdx::Message& message) = 0;
+  virtual void HandleImpulse(pdx::Message& message) = 0;
+
+  // Captures buffer info for use by BufferHubService::DumpState().
+  struct BufferInfo {
+    // Common data field shared by BufferProducer and ProducerQueue.
+    int id = -1;
+    int type = -1;
+    size_t consumer_count = 0;
+
+    // Data field for buffer producer.
+    uint32_t width = 0;
+    uint32_t height = 0;
+    uint32_t layer_count = 0;
+    uint32_t format = 0;
+    uint64_t usage = 0;
+    std::string name;
+
+    // Data filed for producer queue.
+    size_t capacity = 0;
+    UsagePolicy usage_policy{0, 0, 0, 0};
+
+    BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
+               uint32_t layer_count, uint32_t format, uint64_t usage, const std::string& name)
+        : id(id),
+          type(kProducerType),
+          consumer_count(consumer_count),
+          width(width),
+          height(height),
+          layer_count(layer_count),
+          format(format),
+          usage(usage),
+          name(name) {}
+
+    BufferInfo(int id, size_t consumer_count, size_t capacity,
+               const UsagePolicy& usage_policy)
+        : id(id),
+          type(kProducerQueueType),
+          consumer_count(consumer_count),
+          capacity(capacity),
+          usage_policy(usage_policy) {}
+
+    BufferInfo() {}
+  };
+
+  // Returns the buffer info for this buffer.
+  virtual BufferInfo GetBufferInfo() const = 0;
+
+  // Signal the client fd that an ownership change occurred using POLLIN.
+  void SignalAvailable();
+
+  // Clear the ownership change event.
+  void ClearAvailable();
+
+  // Signal hangup event.
+  void Hangup();
+
+  BufferHubService* service() const { return service_; }
+  ChannelType channel_type() const { return channel_type_; }
+  int buffer_id() const { return buffer_id_; }
+
+  int channel_id() const { return channel_id_; }
+  bool IsDetached() const { return channel_id_ == kDetachedId; }
+
+  void Detach() {
+    if (channel_type_ == kProducerType)
+      channel_id_ = kDetachedId;
+  }
+  void Attach(int channel_id) {
+    if (channel_type_ == kProducerType && channel_id_ == kDetachedId)
+      channel_id_ = channel_id;
+  }
+
+ private:
+  BufferHubService* service_;
+
+  // Static id of the buffer for logging and informational purposes. This id
+  // does not change for the life of the buffer.
+  // TODO(eieio): Consider using an id allocator instead of the originating
+  // channel id; channel ids wrap after 2^31 ids, but this is not a problem in
+  // general because channel ids are not used for any lookup in this service.
+  int buffer_id_;
+
+  // The channel id of the buffer. This may change for a persistent producer
+  // buffer if it is detached and re-attached to another channel.
+  int channel_id_;
+
+  ChannelType channel_type_;
+
+  BufferHubChannel(const BufferHubChannel&) = delete;
+  void operator=(const BufferHubChannel&) = delete;
+};
+
+class BufferHubService : public pdx::ServiceBase<BufferHubService> {
+ public:
+  BufferHubService();
+  ~BufferHubService() override;
+
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+  void HandleImpulse(pdx::Message& message) override;
+
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+
+  bool IsInitialized() const override;
+  std::string DumpState(size_t max_length) override;
+
+  bool AddNamedBuffer(const std::string& name,
+                      const std::shared_ptr<ProducerChannel>& buffer);
+  std::shared_ptr<ProducerChannel> GetNamedBuffer(const std::string& name);
+  bool RemoveNamedBuffer(const ProducerChannel& buffer);
+
+ private:
+  friend BASE;
+
+  std::unordered_map<std::string, std::shared_ptr<ProducerChannel>>
+      named_buffers_;
+
+  pdx::Status<void> OnCreateBuffer(pdx::Message& message, uint32_t width,
+                                   uint32_t height, uint32_t format,
+                                   uint64_t usage, size_t meta_size_bytes);
+  pdx::Status<void> OnCreatePersistentBuffer(pdx::Message& message,
+                                             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);
+  pdx::Status<void> OnGetPersistentBuffer(pdx::Message& message,
+                                          const std::string& name);
+  pdx::Status<QueueInfo> OnCreateProducerQueue(pdx::Message& message,
+                                               size_t meta_size_bytes,
+                                               const UsagePolicy& usage_policy);
+
+  BufferHubService(const BufferHubService&) = delete;
+  void operator=(const BufferHubService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
new file mode 100644
index 0000000..d4fc540
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -0,0 +1,37 @@
+#include <sched.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include "buffer_hub.h"
+
+int main(int, char**) {
+  int ret = -1;
+  std::shared_ptr<android::pdx::Service> service;
+  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
+
+  service = android::dvr::BufferHubService::Create();
+  CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
+  dispatcher->AddService(service);
+
+  ret = dvrSetSchedulerClass(0, "graphics");
+  CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
+
+  ALOGI("Entering message loop.");
+
+  ret = dispatcher->EnterDispatchLoop();
+  CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+              strerror(-ret));
+
+error:
+  return -ret;
+}
diff --git a/services/vr/bufferhubd/bufferhubd.rc b/services/vr/bufferhubd/bufferhubd.rc
new file mode 100644
index 0000000..46fe5f9
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.rc
@@ -0,0 +1,6 @@
+service bufferhubd /system/bin/bufferhubd
+  class core
+  user system
+  group system
+  writepid /dev/cpuset/tasks
+  socket pdx/system/buffer_hub/client stream 0660 system system u:object_r:pdx_bufferhub_client_endpoint_socket:s0
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
new file mode 100644
index 0000000..08b2790
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,180 @@
+#include "consumer_channel.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "producer_channel.h"
+
+using android::pdx::ErrorStatus;
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
+                                 int channel_id,
+                                 const std::shared_ptr<Channel> producer)
+    : BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
+      handled_(true),
+      ignored_(false),
+      producer_(producer) {
+  GetProducer()->AddConsumer(this);
+}
+
+ConsumerChannel::~ConsumerChannel() {
+  ALOGD_IF(TRACE,
+           "ConsumerChannel::~ConsumerChannel: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+
+  if (auto producer = GetProducer()) {
+    if (!handled_)  // Producer is waiting for our Release.
+      producer->OnConsumerIgnored();
+    producer->RemoveConsumer(this);
+  }
+}
+
+BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const {
+  BufferHubChannel::BufferInfo info;
+  if (auto producer = GetProducer()) {
+    // If producer has not hung up, copy most buffer info from the producer.
+    info = producer->GetBufferInfo();
+  }
+  info.id = buffer_id();
+  return info;
+}
+
+std::shared_ptr<ProducerChannel> ConsumerChannel::GetProducer() const {
+  return std::static_pointer_cast<ProducerChannel>(producer_.lock());
+}
+
+void ConsumerChannel::HandleImpulse(Message& message) {
+  ATRACE_NAME("ConsumerChannel::HandleImpulse");
+  switch (message.GetOp()) {
+    case BufferHubRPC::ConsumerRelease::Opcode:
+      OnConsumerRelease(message, {});
+      break;
+  }
+}
+
+bool ConsumerChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("ConsumerChannel::HandleMessage");
+  auto producer = GetProducer();
+  if (!producer)
+    REPLY_ERROR_RETURN(message, EPIPE, true);
+
+  switch (message.GetOp()) {
+    case BufferHubRPC::GetBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+          *producer, &ProducerChannel::OnGetBuffer, message);
+      return true;
+
+    case BufferHubRPC::NewConsumer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+          *producer, &ProducerChannel::OnNewConsumer, message);
+      return true;
+
+    case BufferHubRPC::ConsumerAcquire::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerAcquire>(
+          *this, &ConsumerChannel::OnConsumerAcquire, message);
+      return true;
+
+    case BufferHubRPC::ConsumerRelease::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerRelease>(
+          *this, &ConsumerChannel::OnConsumerRelease, message);
+      return true;
+
+    case BufferHubRPC::ConsumerSetIgnore::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
+          *this, &ConsumerChannel::OnConsumerSetIgnore, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+Status<std::pair<BorrowedFence, ConsumerChannel::MetaData>>
+ConsumerChannel::OnConsumerAcquire(Message& message,
+                                   std::size_t metadata_size) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerAcquire");
+  auto producer = GetProducer();
+  if (!producer)
+    return ErrorStatus(EPIPE);
+
+  if (ignored_ || handled_) {
+    ALOGE(
+        "ConsumerChannel::OnConsumerAcquire: Acquire when not posted: "
+        "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+        ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+    return ErrorStatus(EBUSY);
+  } else {
+    ClearAvailable();
+    return producer->OnConsumerAcquire(message, metadata_size);
+  }
+}
+
+Status<void> ConsumerChannel::OnConsumerRelease(Message& message,
+                                                LocalFence release_fence) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerRelease");
+  auto producer = GetProducer();
+  if (!producer)
+    return ErrorStatus(EPIPE);
+
+  if (ignored_ || handled_) {
+    ALOGE(
+        "ConsumerChannel::OnConsumerRelease: Release when not acquired: "
+        "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+        ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+    return ErrorStatus(EBUSY);
+  } else {
+    ClearAvailable();
+    auto status =
+        producer->OnConsumerRelease(message, std::move(release_fence));
+    handled_ = !!status;
+    return status;
+  }
+}
+
+Status<void> ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
+  auto producer = GetProducer();
+  if (!producer)
+    return ErrorStatus(EPIPE);
+
+  ignored_ = ignored;
+  if (ignored_ && !handled_) {
+    // Update the producer if ignore is set after the consumer acquires the
+    // buffer.
+    ClearAvailable();
+    producer->OnConsumerIgnored();
+    handled_ = false;
+  }
+
+  return {};
+}
+
+bool ConsumerChannel::OnProducerPosted() {
+  if (ignored_) {
+    handled_ = true;
+    return false;
+  } else {
+    handled_ = false;
+    SignalAvailable();
+    return true;
+  }
+}
+
+void ConsumerChannel::OnProducerClosed() {
+  producer_.reset();
+  Hangup();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/consumer_channel.h
new file mode 100644
index 0000000..d84055c
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.h
@@ -0,0 +1,52 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+// Consumer channels are attached to a Producer channel
+class ConsumerChannel : public BufferHubChannel {
+ public:
+  using Channel = pdx::Channel;
+  using Message = pdx::Message;
+
+  ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
+                  const std::shared_ptr<Channel> producer);
+  ~ConsumerChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  bool OnProducerPosted();
+  void OnProducerClosed();
+
+ private:
+  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+
+  std::shared_ptr<ProducerChannel> GetProducer() const;
+
+  pdx::Status<std::pair<BorrowedFence, MetaData>> OnConsumerAcquire(
+      Message& message, std::size_t metadata_size);
+  pdx::Status<void> OnConsumerRelease(Message& message,
+                                      LocalFence release_fence);
+  pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore);
+
+  bool handled_;  // True if we have processed RELEASE.
+  bool ignored_;  // True if we are ignoring events.
+  std::weak_ptr<Channel> producer_;
+
+  ConsumerChannel(const ConsumerChannel&) = delete;
+  void operator=(const ConsumerChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
new file mode 100644
index 0000000..f447e00
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -0,0 +1,152 @@
+#include "consumer_queue_channel.h"
+
+#include <pdx/channel_handle.h>
+
+#include "producer_channel.h"
+
+using android::pdx::ErrorStatus;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodError;
+
+namespace android {
+namespace dvr {
+
+ConsumerQueueChannel::ConsumerQueueChannel(
+    BufferHubService* service, int buffer_id, int channel_id,
+    const std::shared_ptr<Channel>& producer)
+    : BufferHubChannel(service, buffer_id, channel_id, kConsumerQueueType),
+      producer_(producer),
+      capacity_(0) {
+  GetProducer()->AddConsumer(this);
+}
+
+ConsumerQueueChannel::~ConsumerQueueChannel() {
+  ALOGD_IF(TRACE, "ConsumerQueueChannel::~ConsumerQueueChannel: channel_id=%d",
+           channel_id());
+
+  if (auto producer = GetProducer()) {
+    producer->RemoveConsumer(this);
+  }
+}
+
+bool ConsumerQueueChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("ConsumerQueueChannel::HandleMessage");
+  auto producer = GetProducer();
+  if (!producer) {
+    RemoteMethodError(message, EPIPE);
+    return true;
+  }
+
+  switch (message.GetOp()) {
+    case BufferHubRPC::CreateConsumerQueue::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+          *producer, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+      return true;
+
+    case BufferHubRPC::GetQueueInfo::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetQueueInfo>(
+          *producer, &ProducerQueueChannel::OnGetQueueInfo, message);
+      return true;
+
+    case BufferHubRPC::ConsumerQueueImportBuffers::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(
+          *this, &ConsumerQueueChannel::OnConsumerQueueImportBuffers, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+std::shared_ptr<ProducerQueueChannel> ConsumerQueueChannel::GetProducer()
+    const {
+  return std::static_pointer_cast<ProducerQueueChannel>(producer_.lock());
+}
+
+void ConsumerQueueChannel::HandleImpulse(Message& /* message */) {
+  ATRACE_NAME("ConsumerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ConsumerQueueChannel::GetBufferInfo() const {
+  BufferHubChannel::BufferInfo info;
+  if (auto producer = GetProducer()) {
+    // If producer has not hung up, copy most buffer info from the producer.
+    info = producer->GetBufferInfo();
+  }
+  info.id = buffer_id();
+  info.capacity = capacity_;
+  return info;
+}
+
+void ConsumerQueueChannel::RegisterNewBuffer(
+    const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
+  ALOGD_IF(TRACE,
+           "ConsumerQueueChannel::RegisterNewBuffer: buffer_id=%d slot=%zu",
+           producer_channel->buffer_id(), slot);
+  pending_buffer_slots_.emplace(producer_channel, slot);
+
+  // Signal the client that there is new buffer available throught POLLIN.
+  SignalAvailable();
+}
+
+Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
+ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
+  std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+  ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
+  ALOGD_IF(
+      TRACE,
+      "ConsumerQueueChannel::OnConsumerQueueImportBuffers number of buffers to "
+      "import: %zu",
+      pending_buffer_slots_.size());
+
+  while (!pending_buffer_slots_.empty()) {
+    auto producer_channel = pending_buffer_slots_.front().first.lock();
+    size_t producer_slot = pending_buffer_slots_.front().second;
+    pending_buffer_slots_.pop();
+
+    // It's possible that the producer channel has expired. When this occurs,
+    // ignore the producer channel.
+    if (producer_channel == nullptr) {
+      ALOGW(
+          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
+          "channel has already been expired.");
+      continue;
+    }
+
+    auto status = producer_channel->CreateConsumer(message);
+
+    // If no buffers are imported successfully, clear available and return an
+    // error. Otherwise, return all consumer handles already imported
+    // successfully, but keep available bits on, so that the client can retry
+    // importing remaining consumer buffers.
+    if (!status) {
+      ALOGE(
+          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: Failed create "
+          "consumer: %s",
+          status.GetErrorMessage().c_str());
+      if (buffer_handles.empty()) {
+        ClearAvailable();
+        return status.error_status();
+      } else {
+        return {std::move(buffer_handles)};
+      }
+    }
+
+    buffer_handles.emplace_back(status.take(), producer_slot);
+  }
+
+  ClearAvailable();
+  return {std::move(buffer_handles)};
+}
+
+void ConsumerQueueChannel::OnProducerClosed() {
+  ALOGD_IF(TRACE, "ConsumerQueueChannel::OnProducerClosed: queue_id=%d",
+           buffer_id());
+  producer_.reset();
+  Hangup();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/consumer_queue_channel.h
new file mode 100644
index 0000000..aa3f531
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+#include <queue>
+
+#include "consumer_channel.h"
+#include "producer_queue_channel.h"
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueueChannel : public BufferHubChannel {
+ public:
+  using Message = pdx::Message;
+  using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+  ConsumerQueueChannel(BufferHubService* service, int buffer_id, int channel_id,
+                       const std::shared_ptr<Channel>& producer);
+  ~ConsumerQueueChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  // Called by ProdcuerQueueChannel to notify consumer queue that a new
+  // buffer has been allocated.
+  void RegisterNewBuffer(
+      const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+
+  // Called after clients been signaled by service that new buffer has been
+  // allocated. Clients uses kOpConsumerQueueImportBuffers to import new
+  // consumer buffers and this handler returns a vector of fd representing
+  // BufferConsumers that clients can import.
+  pdx::Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
+  OnConsumerQueueImportBuffers(Message& message);
+
+  void OnProducerClosed();
+
+ private:
+  std::shared_ptr<ProducerQueueChannel> GetProducer() const;
+
+  // Pointer to the prodcuer channel
+  std::weak_ptr<Channel> producer_;
+
+  // Tracks newly allocated buffer producers along with it's slot number.
+  std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
+      pending_buffer_slots_;
+
+  // Tracks how many buffers have this queue imported.
+  size_t capacity_;
+
+  ConsumerQueueChannel(const ConsumerQueueChannel&) = delete;
+  void operator=(const ConsumerQueueChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
new file mode 100644
index 0000000..b9984a0
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,360 @@
+#include "producer_channel.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+#include <sys/poll.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <atomic>
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::WrapBuffer;
+
+namespace android {
+namespace dvr {
+
+ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id,
+                                 uint32_t width, uint32_t height,
+                                 uint32_t layer_count, uint32_t format,
+                                 uint64_t usage, size_t meta_size_bytes,
+                                 int* error)
+    : BufferHubChannel(service, channel_id, channel_id, kProducerType),
+      pending_consumers_(0),
+      producer_owns_(true),
+      meta_size_bytes_(meta_size_bytes),
+      meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) {
+  const int ret = buffer_.Alloc(width, height, layer_count, format, usage);
+  if (ret < 0) {
+    ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
+          strerror(-ret));
+    *error = ret;
+    return;
+  }
+
+  // Success.
+  *error = 0;
+}
+
+Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create(
+    BufferHubService* service, int channel_id, uint32_t width, uint32_t height,
+    uint32_t layer_count, uint32_t format, uint64_t usage,
+    size_t meta_size_bytes) {
+  int error;
+  std::shared_ptr<ProducerChannel> producer(
+      new ProducerChannel(service, channel_id, width, height, layer_count,
+                          format, usage, meta_size_bytes, &error));
+  if (error < 0)
+    return ErrorStatus(-error);
+  else
+    return {std::move(producer)};
+}
+
+ProducerChannel::~ProducerChannel() {
+  ALOGD_IF(TRACE,
+           "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  for (auto consumer : consumer_channels_)
+    consumer->OnProducerClosed();
+}
+
+BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
+  return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
+                    buffer_.height(), buffer_.layer_count(), buffer_.format(),
+                    buffer_.usage(), name_);
+}
+
+void ProducerChannel::HandleImpulse(Message& message) {
+  ATRACE_NAME("ProducerChannel::HandleImpulse");
+  switch (message.GetOp()) {
+    case BufferHubRPC::ProducerGain::Opcode:
+      OnProducerGain(message);
+      break;
+  }
+}
+
+bool ProducerChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("ProducerChannel::HandleMessage");
+  switch (message.GetOp()) {
+    case BufferHubRPC::GetBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+          *this, &ProducerChannel::OnGetBuffer, message);
+      return true;
+
+    case BufferHubRPC::NewConsumer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+          *this, &ProducerChannel::OnNewConsumer, message);
+      return true;
+
+    case BufferHubRPC::ProducerPost::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerPost>(
+          *this, &ProducerChannel::OnProducerPost, message);
+      return true;
+
+    case BufferHubRPC::ProducerGain::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerGain>(
+          *this, &ProducerChannel::OnProducerGain, message);
+      return true;
+
+    case BufferHubRPC::ProducerMakePersistent::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerMakePersistent>(
+          *this, &ProducerChannel::OnProducerMakePersistent, message);
+      return true;
+
+    case BufferHubRPC::ProducerRemovePersistence::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerRemovePersistence>(
+          *this, &ProducerChannel::OnRemovePersistence, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+Status<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffer(
+    Message& message) {
+  ATRACE_NAME("ProducerChannel::OnGetBuffer");
+  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id());
+  return {NativeBufferHandle<BorrowedHandle>(buffer_, buffer_id())};
+}
+
+Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
+  ATRACE_NAME("ProducerChannel::CreateConsumer");
+  ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id());
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  auto consumer = std::make_shared<ConsumerChannel>(
+      service(), buffer_id(), channel_id, shared_from_this());
+  const auto channel_status = service()->SetChannel(channel_id, consumer);
+  if (!channel_status) {
+    ALOGE(
+        "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
+        "%s",
+        channel_status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  if (!producer_owns_) {
+    // Signal the new consumer when adding it to a posted producer.
+    if (consumer->OnProducerPosted())
+      pending_consumers_++;
+  }
+
+  return {status.take()};
+}
+
+Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) {
+  ATRACE_NAME("ProducerChannel::OnNewConsumer");
+  ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
+  return CreateConsumer(message);
+}
+
+Status<void> ProducerChannel::OnProducerPost(
+    Message&, LocalFence acquire_fence,
+    BufferWrapper<std::vector<std::uint8_t>> metadata) {
+  ATRACE_NAME("ProducerChannel::OnProducerPost");
+  ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
+  if (!producer_owns_) {
+    ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
+    return ErrorStatus(EBUSY);
+  }
+
+  if (meta_size_bytes_ != metadata.size()) {
+    ALOGD_IF(TRACE,
+             "ProducerChannel::OnProducerPost: Expected meta_size_bytes=%zu "
+             "got size=%zu",
+             meta_size_bytes_, metadata.size());
+    return ErrorStatus(EINVAL);
+  }
+
+  std::copy(metadata.begin(), metadata.end(), meta_.get());
+  post_fence_ = std::move(acquire_fence);
+  producer_owns_ = false;
+
+  // Signal any interested consumers. If there are none, automatically release
+  // the buffer.
+  pending_consumers_ = 0;
+  for (auto consumer : consumer_channels_) {
+    if (consumer->OnProducerPosted())
+      pending_consumers_++;
+  }
+  if (pending_consumers_ == 0)
+    SignalAvailable();
+  ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
+           pending_consumers_);
+
+  return {};
+}
+
+Status<LocalFence> ProducerChannel::OnProducerGain(Message& message) {
+  ATRACE_NAME("ProducerChannel::OnGain");
+  ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
+  if (producer_owns_) {
+    ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
+          channel_id());
+    return ErrorStatus(EALREADY);
+  }
+
+  // There are still pending consumers, return busy.
+  if (pending_consumers_ > 0)
+    return ErrorStatus(EBUSY);
+
+  ClearAvailable();
+  producer_owns_ = true;
+  post_fence_.close();
+  return {std::move(returned_fence_)};
+}
+
+Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>>
+ProducerChannel::OnConsumerAcquire(Message& message,
+                                   std::size_t metadata_size) {
+  ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
+  ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
+           buffer_id());
+  if (producer_owns_) {
+    ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
+    return ErrorStatus(EBUSY);
+  }
+
+  // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
+  // Serialization just needs to read the handle.
+  if (metadata_size == 0)
+    return {std::make_pair(post_fence_.borrow(),
+                           WrapBuffer<std::uint8_t>(nullptr, 0))};
+  else
+    return {std::make_pair(post_fence_.borrow(),
+                           WrapBuffer(meta_.get(), meta_size_bytes_))};
+}
+
+Status<void> ProducerChannel::OnConsumerRelease(Message&,
+                                                LocalFence release_fence) {
+  ATRACE_NAME("ProducerChannel::OnConsumerRelease");
+  ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
+           buffer_id());
+  if (producer_owns_) {
+    ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
+    return ErrorStatus(EBUSY);
+  }
+
+  // Attempt to merge the fences if necessary.
+  if (release_fence) {
+    if (returned_fence_) {
+      LocalFence merged_fence(sync_merge("bufferhub_merged",
+                                         returned_fence_.get_fd(),
+                                         release_fence.get_fd()));
+      const int error = errno;
+      if (!merged_fence) {
+        ALOGE("ProducerChannel::OnConsumerRelease: Failed to merge fences: %s",
+              strerror(error));
+        return ErrorStatus(error);
+      }
+      returned_fence_ = std::move(merged_fence);
+    } else {
+      returned_fence_ = std::move(release_fence);
+    }
+  }
+
+  OnConsumerIgnored();
+  return {};
+}
+
+void ProducerChannel::OnConsumerIgnored() {
+  if (!--pending_consumers_)
+    SignalAvailable();
+  ALOGD_IF(TRACE,
+           "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
+           buffer_id(), pending_consumers_);
+}
+
+Status<void> ProducerChannel::OnProducerMakePersistent(Message& message,
+                                                       const std::string& name,
+                                                       int user_id,
+                                                       int group_id) {
+  ATRACE_NAME("ProducerChannel::OnProducerMakePersistent");
+  ALOGD_IF(TRACE,
+           "ProducerChannel::OnProducerMakePersistent: buffer_id=%d name=%s "
+           "user_id=%d group_id=%d",
+           buffer_id(), name.c_str(), user_id, group_id);
+
+  if (name.empty() || (user_id < 0 && user_id != kNoCheckId) ||
+      (group_id < 0 && group_id != kNoCheckId)) {
+    return ErrorStatus(EINVAL);
+  }
+
+  // Try to add this buffer with the requested name.
+  if (service()->AddNamedBuffer(name, std::static_pointer_cast<ProducerChannel>(
+                                          shared_from_this()))) {
+    // If successful, set the requested permissions.
+
+    // A value of zero indicates that the ids from the sending process should be
+    // used.
+    if (user_id == kUseCallerId)
+      user_id = message.GetEffectiveUserId();
+    if (group_id == kUseCallerId)
+      group_id = message.GetEffectiveGroupId();
+
+    owner_user_id_ = user_id;
+    owner_group_id_ = group_id;
+    name_ = name;
+    return {};
+  } else {
+    // Otherwise a buffer with that name already exists.
+    return ErrorStatus(EALREADY);
+  }
+}
+
+Status<void> ProducerChannel::OnRemovePersistence(Message&) {
+  if (service()->RemoveNamedBuffer(*this))
+    return {};
+  else
+    return ErrorStatus(ENOENT);
+}
+
+void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
+  consumer_channels_.push_back(channel);
+}
+
+void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
+  consumer_channels_.erase(
+      std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+// Returns true if either the user or group ids match the owning ids or both
+// owning ids are not set, in which case access control does not apply.
+bool ProducerChannel::CheckAccess(int euid, int egid) {
+  const bool no_check =
+      owner_user_id_ == kNoCheckId && owner_group_id_ == kNoCheckId;
+  const bool euid_check = euid == owner_user_id_ || euid == kRootId;
+  const bool egid_check = egid == owner_group_id_ || egid == kRootId;
+  return no_check || euid_check || egid_check;
+}
+
+// Returns true if the given parameters match the underlying buffer parameters.
+bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
+                                      uint32_t layer_count, uint32_t format,
+                                      uint64_t usage, size_t meta_size_bytes) {
+  return meta_size_bytes == meta_size_bytes_ && buffer_.width() == width &&
+         buffer_.height() == height && buffer_.layer_count() == layer_count &&
+         buffer_.format() == format && buffer_.usage() == usage;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
new file mode 100644
index 0000000..5ada478
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -0,0 +1,109 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// The buffer changes ownership according to the following sequence:
+// POST -> ACQUIRE/RELEASE (all consumers) -> GAIN (producer acquires) -> POST
+
+// The producer channel is owned by a single app that writes into buffers and
+// calls POST when drawing is complete. This channel has a set of consumer
+// channels associated with it that are waiting for notifications.
+class ProducerChannel : public BufferHubChannel {
+ public:
+  using Message = pdx::Message;
+  using BorrowedHandle = pdx::BorrowedHandle;
+  using RemoteChannelHandle = pdx::RemoteChannelHandle;
+  template <typename T>
+  using BufferWrapper = pdx::rpc::BufferWrapper<T>;
+
+  static pdx::Status<std::shared_ptr<ProducerChannel>> Create(
+      BufferHubService* service, int channel_id, uint32_t width,
+      uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage,
+      size_t meta_size_bytes);
+
+  ~ProducerChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  pdx::Status<NativeBufferHandle<BorrowedHandle>> OnGetBuffer(Message& message);
+
+  pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message);
+  pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message);
+
+  pdx::Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>>
+  OnConsumerAcquire(Message& message, std::size_t metadata_size);
+  pdx::Status<void> OnConsumerRelease(Message& message,
+                                      LocalFence release_fence);
+
+  void OnConsumerIgnored();
+
+  void AddConsumer(ConsumerChannel* channel);
+  void RemoveConsumer(ConsumerChannel* channel);
+
+  bool CheckAccess(int euid, int egid);
+  bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
+                       uint32_t format, uint64_t usage, size_t meta_size_bytes);
+
+  pdx::Status<void> OnProducerMakePersistent(Message& message,
+                                             const std::string& name,
+                                             int user_id, int group_id);
+  pdx::Status<void> OnRemovePersistence(Message& message);
+
+ private:
+  std::vector<ConsumerChannel*> consumer_channels_;
+  // This counts the number of consumers left to process this buffer. If this is
+  // zero then the producer can re-acquire ownership.
+  int pending_consumers_;
+
+  IonBuffer buffer_;
+
+  bool producer_owns_;
+  LocalFence post_fence_;
+  LocalFence returned_fence_;
+  size_t meta_size_bytes_;
+  std::unique_ptr<uint8_t[]> meta_;
+
+  static constexpr int kNoCheckId = -1;
+  static constexpr int kUseCallerId = 0;
+  static constexpr int kRootId = 0;
+
+  // User and group id to check when obtaining a persistent buffer.
+  int owner_user_id_ = kNoCheckId;
+  int owner_group_id_ = kNoCheckId;
+
+  std::string name_;
+
+  ProducerChannel(BufferHubService* service, int channel, uint32_t width,
+                  uint32_t height, uint32_t layer_count, uint32_t format,
+                  uint64_t usage, size_t meta_size_bytes, int* error);
+
+  pdx::Status<void> OnProducerPost(
+      Message& message, LocalFence acquire_fence,
+      BufferWrapper<std::vector<std::uint8_t>> metadata);
+  pdx::Status<LocalFence> OnProducerGain(Message& message);
+
+  ProducerChannel(const ProducerChannel&) = delete;
+  void operator=(const ProducerChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
new file mode 100644
index 0000000..886e621
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -0,0 +1,311 @@
+#include "producer_queue_channel.h"
+
+#include <inttypes.h>
+
+#include "consumer_queue_channel.h"
+#include "producer_channel.h"
+
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Status;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ProducerQueueChannel::ProducerQueueChannel(BufferHubService* service,
+                                           int channel_id,
+                                           size_t meta_size_bytes,
+                                           const UsagePolicy& usage_policy,
+                                           int* error)
+    : BufferHubChannel(service, channel_id, channel_id, kProducerQueueType),
+      meta_size_bytes_(meta_size_bytes),
+      usage_policy_(usage_policy),
+      capacity_(0) {
+  *error = 0;
+}
+
+ProducerQueueChannel::~ProducerQueueChannel() {
+  ALOGD_IF(TRACE, "ProducerQueueChannel::~ProducerQueueChannel: queue_id=%d",
+           buffer_id());
+  for (auto* consumer : consumer_channels_)
+    consumer->OnProducerClosed();
+}
+
+/* static */
+Status<std::shared_ptr<ProducerQueueChannel>> ProducerQueueChannel::Create(
+    BufferHubService* service, int channel_id, size_t meta_size_bytes,
+    const UsagePolicy& usage_policy) {
+  // Configuration between |usage_deny_set_mask| and |usage_deny_clear_mask|
+  // should be mutually exclusive.
+  if ((usage_policy.usage_deny_set_mask & usage_policy.usage_deny_clear_mask)) {
+    ALOGE(
+        "BufferHubService::OnCreateProducerQueue: illegal usage mask "
+        "configuration: usage_deny_set_mask=%" PRIx64
+        " usage_deny_clear_mask=%" PRIx64,
+        usage_policy.usage_deny_set_mask, usage_policy.usage_deny_clear_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  int error = 0;
+  std::shared_ptr<ProducerQueueChannel> producer(new ProducerQueueChannel(
+      service, channel_id, meta_size_bytes, usage_policy, &error));
+  if (error < 0)
+    return ErrorStatus(-error);
+  else
+    return {std::move(producer)};
+}
+
+bool ProducerQueueChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("ProducerQueueChannel::HandleMessage");
+  switch (message.GetOp()) {
+    case BufferHubRPC::CreateConsumerQueue::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+          *this, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+      return true;
+
+    case BufferHubRPC::GetQueueInfo::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetQueueInfo>(
+          *this, &ProducerQueueChannel::OnGetQueueInfo, message);
+      return true;
+
+    case BufferHubRPC::ProducerQueueAllocateBuffers::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+          *this, &ProducerQueueChannel::OnProducerQueueAllocateBuffers,
+          message);
+      return true;
+
+    case BufferHubRPC::ProducerQueueDetachBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(
+          *this, &ProducerQueueChannel::OnProducerQueueDetachBuffer, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+void ProducerQueueChannel::HandleImpulse(Message& /* message */) {
+  ATRACE_NAME("ProducerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ProducerQueueChannel::GetBufferInfo() const {
+  return BufferInfo(channel_id(), consumer_channels_.size(), capacity_,
+                    usage_policy_);
+}
+
+Status<RemoteChannelHandle> ProducerQueueChannel::OnCreateConsumerQueue(
+    Message& message) {
+  ATRACE_NAME("ProducerQueueChannel::OnCreateConsumerQueue");
+  ALOGD_IF(TRACE, "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d",
+           channel_id());
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "ProducerQueueChannel::OnCreateConsumerQueue: failed to push consumer "
+        "channel: %s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  auto consumer_queue_channel = std::make_shared<ConsumerQueueChannel>(
+      service(), buffer_id(), channel_id, shared_from_this());
+
+  // Register the existing buffers with the new consumer queue.
+  for (size_t slot = 0; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+    if (auto buffer = buffers_[slot].lock())
+      consumer_queue_channel->RegisterNewBuffer(buffer, slot);
+  }
+
+  const auto channel_status =
+      service()->SetChannel(channel_id, consumer_queue_channel);
+  if (!channel_status) {
+    ALOGE(
+        "ProducerQueueChannel::OnCreateConsumerQueue: Failed to set channel: "
+        "%s",
+        channel_status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  return {status.take()};
+}
+
+Status<QueueInfo> ProducerQueueChannel::OnGetQueueInfo(Message&) {
+  return {{meta_size_bytes_, buffer_id()}};
+}
+
+Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
+ProducerQueueChannel::OnProducerQueueAllocateBuffers(
+    Message& message, uint32_t width, uint32_t height, uint32_t layer_count,
+    uint32_t format, uint64_t usage, size_t buffer_count) {
+  ATRACE_NAME("ProducerQueueChannel::OnProducerQueueAllocateBuffers");
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::OnProducerQueueAllocateBuffers: "
+           "producer_channel_id=%d",
+           channel_id());
+
+  std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+
+  // Deny buffer allocation violating preset rules.
+  if (usage & usage_policy_.usage_deny_set_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %" PRIx64
+        " is not permitted. Violating usage_deny_set_mask, the following  bits "
+        "shall not be set: %" PRIx64 ".",
+        usage, usage_policy_.usage_deny_set_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  if (~usage & usage_policy_.usage_deny_clear_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %" PRIx64
+        " is not permitted. Violating usage_deny_clear_mask, the following "
+        "bits must be set: %" PRIx64 ".",
+        usage, usage_policy_.usage_deny_clear_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  // Force set mask and clear mask. Note that |usage_policy_.usage_set_mask_|
+  // takes precedence and will overwrite |usage_policy_.usage_clear_mask|.
+  uint64_t effective_usage =
+      (usage & ~usage_policy_.usage_clear_mask) | usage_policy_.usage_set_mask;
+
+  for (size_t i = 0; i < buffer_count; i++) {
+    auto status = AllocateBuffer(message, width, height, layer_count, format,
+                                 effective_usage);
+    if (!status) {
+      ALOGE(
+          "ProducerQueueChannel::OnProducerQueueAllocateBuffers: Failed to "
+          "allocate new buffer.");
+      return ErrorStatus(status.error());
+    }
+    buffer_handles.push_back(status.take());
+  }
+
+  return {std::move(buffer_handles)};
+}
+
+Status<std::pair<RemoteChannelHandle, size_t>>
+ProducerQueueChannel::AllocateBuffer(Message& message, uint32_t width,
+                                     uint32_t height, uint32_t layer_count,
+                                     uint32_t format, uint64_t usage) {
+  ATRACE_NAME("ProducerQueueChannel::AllocateBuffer");
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::AllocateBuffer: producer_channel_id=%d",
+           channel_id());
+
+  if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE("ProducerQueueChannel::AllocateBuffer: reaches kMaxQueueCapacity.");
+    return ErrorStatus(E2BIG);
+  }
+
+  // Here we are creating a new BufferHubBuffer, initialize the producer
+  // channel, and returning its file handle back to the client.
+  // buffer_id is the id of the producer channel of BufferHubBuffer.
+  int buffer_id;
+  auto status = message.PushChannel(0, nullptr, &buffer_id);
+
+  if (!status) {
+    ALOGE("ProducerQueueChannel::AllocateBuffer: failed to push channel: %s",
+          status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  }
+
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::AllocateBuffer: buffer_id=%d width=%u "
+           "height=%u layer_count=%u format=%u usage=%" PRIx64,
+           buffer_id, width, height, layer_count, format, usage);
+  auto buffer_handle = status.take();
+
+  auto producer_channel_status =
+      ProducerChannel::Create(service(), buffer_id, width, height, layer_count,
+                              format, usage, meta_size_bytes_);
+  if (!producer_channel_status) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: Failed to create producer "
+        "buffer: %s",
+        producer_channel_status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+  auto producer_channel = producer_channel_status.take();
+
+  ALOGD_IF(
+      TRACE,
+      "ProducerQueueChannel::AllocateBuffer: buffer_id=%d, buffer_handle=%d",
+      buffer_id, buffer_handle.value());
+
+  const auto channel_status =
+      service()->SetChannel(buffer_id, producer_channel);
+  if (!channel_status) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: failed to set producer channel "
+        "for new BufferHubBuffer: %s",
+        channel_status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  // Register the newly allocated buffer's channel_id into the first empty
+  // buffer slot.
+  size_t slot = 0;
+  for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+    if (buffers_[slot].expired())
+      break;
+  }
+  if (slot == BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new "
+        "buffer allocation.");
+    return ErrorStatus(E2BIG);
+  }
+
+  buffers_[slot] = producer_channel;
+  capacity_++;
+
+  // Notify each consumer channel about the new buffer.
+  for (auto* consumer_channel : consumer_channels_) {
+    ALOGD(
+        "ProducerQueueChannel::AllocateBuffer: Notified consumer with new "
+        "buffer, buffer_id=%d",
+        buffer_id);
+    consumer_channel->RegisterNewBuffer(producer_channel, slot);
+  }
+
+  return {{std::move(buffer_handle), slot}};
+}
+
+Status<void> ProducerQueueChannel::OnProducerQueueDetachBuffer(
+    Message& /*message*/, size_t slot) {
+  if (buffers_[slot].expired()) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach "
+        "an invalid buffer producer at slot %zu",
+        slot);
+    return ErrorStatus(EINVAL);
+  }
+
+  if (capacity_ == 0) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach a "
+        "buffer producer while the queue's capacity is already zero.");
+    return ErrorStatus(EINVAL);
+  }
+
+  buffers_[slot].reset();
+  capacity_--;
+  return {};
+}
+
+void ProducerQueueChannel::AddConsumer(ConsumerQueueChannel* channel) {
+  consumer_channels_.push_back(channel);
+}
+
+void ProducerQueueChannel::RemoveConsumer(ConsumerQueueChannel* channel) {
+  consumer_channels_.erase(
+      std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h
new file mode 100644
index 0000000..28c74cd
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -0,0 +1,92 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <pdx/status.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class ProducerQueueChannel : public BufferHubChannel {
+ public:
+  static pdx::Status<std::shared_ptr<ProducerQueueChannel>> Create(
+      BufferHubService* service, int channel_id, size_t meta_size_bytes,
+      const UsagePolicy& usage_policy);
+  ~ProducerQueueChannel() override;
+
+  bool HandleMessage(pdx::Message& message) override;
+  void HandleImpulse(pdx::Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  // Handles client request to create a new consumer queue attached to current
+  // producer queue.
+  // Returns a handle for the service channel, as well as the size of the
+  // metadata associated with the queue.
+  pdx::Status<pdx::RemoteChannelHandle> OnCreateConsumerQueue(
+      pdx::Message& message);
+
+  pdx::Status<QueueInfo> OnGetQueueInfo(pdx::Message& message);
+
+  // Allocate a new BufferHubProducer according to the input spec. Client may
+  // handle this as if a new producer is created through kOpCreateBuffer.
+  pdx::Status<std::vector<std::pair<pdx::RemoteChannelHandle, size_t>>>
+  OnProducerQueueAllocateBuffers(pdx::Message& message, uint32_t width,
+                                 uint32_t height, uint32_t layer_count,
+                                 uint32_t format, uint64_t usage,
+                                 size_t buffer_count);
+
+  // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must
+  // be in Gain'ed state for the producer queue to detach.
+  pdx::Status<void> OnProducerQueueDetachBuffer(pdx::Message& message,
+                                                size_t slot);
+
+  void AddConsumer(ConsumerQueueChannel* channel);
+  void RemoveConsumer(ConsumerQueueChannel* channel);
+
+ private:
+  ProducerQueueChannel(BufferHubService* service, int channel_id,
+                       size_t meta_size_bytes, const UsagePolicy& usage_policy,
+                       int* error);
+
+  // Allocate one single producer buffer by |OnProducerQueueAllocateBuffers|.
+  // Note that the newly created buffer's file handle will be pushed to client
+  // and our return type is a RemoteChannelHandle.
+  // Returns the remote channel handle and the slot number for the newly
+  // allocated buffer.
+  pdx::Status<std::pair<pdx::RemoteChannelHandle, size_t>> AllocateBuffer(
+      pdx::Message& message, uint32_t width, uint32_t height,
+      uint32_t layer_count, uint32_t format, uint64_t usage);
+
+  // Size of the meta data associated with all the buffers allocated from the
+  // queue. Now we assume the metadata size is immutable once the queue is
+  // created.
+  size_t meta_size_bytes_;
+
+  // A set of variables to control what |usage| bits can this ProducerQueue
+  // allocate.
+  UsagePolicy usage_policy_;
+
+  // Provides access to the |channel_id| of all consumer channels associated
+  // with this producer.
+  std::vector<ConsumerQueueChannel*> consumer_channels_;
+
+  // Tracks how many buffers have this queue allocated.
+  size_t capacity_;
+
+  // Tracks of all buffer producer allocated through this buffer queue. Once
+  // a buffer get allocated, it will take a logical slot in the |buffers_| array
+  // and the slot number will stay unchanged during the entire life cycle of the
+  // queue.
+  std::weak_ptr<ProducerChannel> buffers_[BufferHubRPC::kMaxQueueCapacity];
+
+  ProducerQueueChannel(const ProducerQueueChannel&) = delete;
+  void operator=(const ProducerQueueChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
new file mode 100644
index 0000000..5cb201d
--- /dev/null
+++ b/services/vr/hardware_composer/Android.bp
@@ -0,0 +1,146 @@
+cc_library_shared {
+  name: "libvr_hwc-hal",
+
+  srcs: [
+    "impl/vr_hwc.cpp",
+    "impl/vr_composer_client.cpp",
+  ],
+
+  static_libs: [
+    "libhwcomposer-client",
+    "libdisplay",
+    "libbufferhubqueue",
+    "libbufferhub",
+    "libpdx_default_transport",
+  ],
+
+  shared_libs: [
+    "android.frameworks.vr.composer@1.0",
+    "android.hardware.graphics.composer@2.1",
+    "android.hardware.graphics.mapper@2.0",
+    "libbase",
+    "libcutils",
+    "libfmq",
+    "libhardware",
+    "libhidlbase",
+    "libhidltransport",
+    "liblog",
+    "libsync",
+    "libui",
+    "libutils",
+  ],
+
+  export_static_lib_headers: [
+    "libhwcomposer-client",
+  ],
+
+  export_shared_lib_headers: [
+    "android.frameworks.vr.composer@1.0",
+    "android.hardware.graphics.composer@2.1",
+  ],
+
+  export_include_dirs: ["."],
+
+  cflags: [
+    "-DLOG_TAG=\"vr_hwc\"",
+  ],
+
+}
+
+cc_library_static {
+  name: "libvr_hwc-binder",
+  srcs: [
+    "aidl/android/dvr/IVrComposer.aidl",
+    "aidl/android/dvr/IVrComposerCallback.aidl",
+    "aidl/android/dvr/parcelable_composer_frame.cpp",
+    "aidl/android/dvr/parcelable_composer_layer.cpp",
+    "aidl/android/dvr/parcelable_unique_fd.cpp",
+  ],
+  aidl: {
+    include_dirs: ["frameworks/native/services/vr/hardware_composer/aidl"],
+    export_aidl_headers: true,
+  },
+  export_include_dirs: ["aidl"],
+
+  shared_libs: [
+    "libbinder",
+    "libui",
+    "libutils",
+    "libvr_hwc-hal",
+  ],
+}
+
+cc_library_static {
+  name: "libvr_hwc-impl",
+  srcs: [
+    "vr_composer.cpp",
+  ],
+  static_libs: [
+    "libvr_hwc-binder",
+  ],
+  shared_libs: [
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libui",
+    "libutils",
+    "libvr_hwc-hal",
+  ],
+  export_shared_lib_headers: [
+    "libvr_hwc-hal",
+  ],
+  cflags: [
+    "-DLOG_TAG=\"vr_hwc\"",
+  ],
+}
+
+cc_binary {
+  name: "vr_hwc",
+  srcs: [
+    "vr_hardware_composer_service.cpp"
+  ],
+  static_libs: [
+    "libvr_hwc-impl",
+    // NOTE: This needs to be included after the *-impl lib otherwise the
+    // symbols in the *-binder library get optimized out.
+    "libvr_hwc-binder",
+  ],
+  shared_libs: [
+    "android.frameworks.vr.composer@1.0",
+    "android.hardware.graphics.composer@2.1",
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libhardware",
+    "libhwbinder",
+    "libui",
+    "libutils",
+    "libvr_hwc-hal",
+  ],
+  cflags: [
+    "-DLOG_TAG=\"vr_hwc\"",
+  ],
+  init_rc: [
+    "vr_hwc.rc",
+  ],
+}
+
+cc_test {
+  name: "vr_hwc_test",
+  gtest: true,
+  srcs: ["tests/vr_composer_test.cpp"],
+  static_libs: [
+    "libgtest",
+    "libvr_hwc-impl",
+    // NOTE: This needs to be included after the *-impl lib otherwise the
+    // symbols in the *-binder library get optimized out.
+    "libvr_hwc-binder",
+  ],
+  shared_libs: [
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libui",
+    "libutils",
+  ],
+}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
new file mode 100644
index 0000000..5fd5c36
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
@@ -0,0 +1,20 @@
+package android.dvr;
+
+import android.dvr.IVrComposerCallback;
+
+/**
+ * Service interface exposed by VR HWC exposed to system apps which allows one
+ * system app to connect to get SurfaceFlinger's outputs (all displays). This
+ * is active when SurfaceFlinger is in VR mode, where all 2D output is
+ * redirected to VR HWC.
+ *
+ * @hide */
+interface IVrComposer
+{
+  const String SERVICE_NAME = "vr_hwc";
+
+  /**
+   * Registers a callback used to receive frame notifications.
+   */
+  void registerObserver(in IVrComposerCallback callback);
+}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
new file mode 100644
index 0000000..aa70de1
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
@@ -0,0 +1,22 @@
+package android.dvr;
+
+import android.dvr.ParcelableComposerFrame;
+import android.dvr.ParcelableUniqueFd;
+
+/**
+ * A system app will implement and register this callback with VRComposer
+ * to receive the layers SurfaceFlinger presented when in VR mode.
+ *
+ * @hide */
+interface IVrComposerCallback {
+  /**
+   * Called by the VR HWC service when a new frame is ready to be presented.
+   *
+   * @param frame The new frame VR HWC wants to present.
+   * @return A fence FD used to signal when the previous frame is no longer
+   * used by the client. This may be an invalid fence (-1) if the client is not
+   * using the previous frame, in which case the previous frame may be re-used
+   * at any point in time.
+   */
+  ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame);
+}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
new file mode 100644
index 0000000..84abc19
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
@@ -0,0 +1,3 @@
+package android.dvr;
+
+parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
new file mode 100644
index 0000000..a200345
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
@@ -0,0 +1,3 @@
+package android.dvr;
+
+parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
new file mode 100644
index 0000000..eee9d13
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
@@ -0,0 +1,3 @@
+package android.dvr;
+
+parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
new file mode 100644
index 0000000..db7d5dc
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
@@ -0,0 +1,110 @@
+#include "aidl/android/dvr/parcelable_composer_frame.h"
+
+#include <binder/Parcel.h>
+
+#include "aidl/android/dvr/parcelable_composer_layer.h"
+
+namespace android {
+namespace dvr {
+
+ParcelableComposerFrame::ParcelableComposerFrame() {}
+
+ParcelableComposerFrame::ParcelableComposerFrame(
+    const ComposerView::Frame& frame)
+    : frame_(frame) {}
+
+ParcelableComposerFrame::~ParcelableComposerFrame() {}
+
+status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const {
+  status_t ret = parcel->writeUint64(frame_.display_id);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(frame_.display_width);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(frame_.display_height);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeBool(frame_.removed);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config));
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode));
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode));
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled));
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(frame_.color_transform_hint);
+  if (ret != OK) return ret;
+
+  for(size_t i = 0; i < 16; i++) {
+    ret = parcel->writeFloat(frame_.color_transform[i]);
+    if (ret != OK) return ret;
+  }
+
+  std::vector<ParcelableComposerLayer> layers;
+  for (size_t i = 0; i < frame_.layers.size(); ++i)
+    layers.push_back(ParcelableComposerLayer(frame_.layers[i]));
+
+  ret = parcel->writeParcelableVector(layers);
+
+  return ret;
+}
+
+status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) {
+  status_t ret = parcel->readUint64(&frame_.display_id);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&frame_.display_width);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&frame_.display_height);
+  if (ret != OK) return ret;
+
+  ret = parcel->readBool(&frame_.removed);
+  if (ret != OK) return ret;
+
+  uint32_t value;
+  ret = parcel->readUint32(&value);
+  if (ret != OK) return ret;
+  frame_.active_config = static_cast<Config>(value);
+
+  ret = parcel->readUint32(&value);
+  if (ret != OK) return ret;
+  frame_.color_mode = static_cast<ColorMode>(value);
+
+  ret = parcel->readUint32(&value);
+  if (ret != OK) return ret;
+  frame_.power_mode = static_cast<IComposerClient::PowerMode>(value);
+
+  ret = parcel->readUint32(&value);
+  if (ret != OK) return ret;
+  frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value);
+
+  ret = parcel->readInt32(&frame_.color_transform_hint);
+  if (ret != OK) return ret;
+
+  for(size_t i = 0; i < 16; i++) {
+    ret = parcel->readFloat(&frame_.color_transform[i]);
+    if (ret != OK) return ret;
+  }
+
+  std::vector<ParcelableComposerLayer> layers;
+  ret = parcel->readParcelableVector(&layers);
+  if (ret != OK) return ret;
+
+  frame_.layers.clear();
+  for (size_t i = 0; i < layers.size(); ++i)
+    frame_.layers.push_back(layers[i].layer());
+
+  return ret;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
new file mode 100644
index 0000000..b478bb5
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
+
+#include <binder/Parcelable.h>
+#include <impl/vr_hwc.h>
+
+namespace android {
+namespace dvr {
+
+class ParcelableComposerFrame : public Parcelable {
+ public:
+  ParcelableComposerFrame();
+  ParcelableComposerFrame(const ComposerView::Frame& frame);
+  ~ParcelableComposerFrame() override;
+
+  ComposerView::Frame frame() const { return frame_; }
+
+  status_t writeToParcel(Parcel* parcel) const override;
+  status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+  ComposerView::Frame frame_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
new file mode 100644
index 0000000..c3621eb
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
@@ -0,0 +1,240 @@
+#include "aidl/android/dvr/parcelable_composer_layer.h"
+
+#include <binder/Parcel.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace dvr {
+
+ParcelableComposerLayer::ParcelableComposerLayer() {}
+
+ParcelableComposerLayer::ParcelableComposerLayer(
+    const ComposerView::ComposerLayer& layer) : layer_(layer) {}
+
+ParcelableComposerLayer::~ParcelableComposerLayer() {}
+
+status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const {
+  status_t ret = parcel->writeUint64(layer_.id);
+  if (ret != OK) return ret;
+
+  ret = parcel->write(*layer_.buffer);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeBool(layer_.fence->isValid());
+  if (ret != OK) return ret;
+
+  if (layer_.fence->isValid()) {
+    ret = parcel->writeFileDescriptor(layer_.fence->dup(), true);
+    if (ret != OK) return ret;
+  }
+
+  ret = parcel->writeInt32(layer_.display_frame.left);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.display_frame.top);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.display_frame.right);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.display_frame.bottom);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeFloat(layer_.crop.left);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeFloat(layer_.crop.top);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeFloat(layer_.crop.right);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeFloat(layer_.crop.bottom);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode));
+  if (ret != OK) return ret;
+
+  ret = parcel->writeFloat(layer_.alpha);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(layer_.type);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(layer_.app_id);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(layer_.z_order);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.cursor_x);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.cursor_y);
+  if (ret != OK) return ret;
+
+  uint32_t color = layer_.color.r |
+      (static_cast<uint32_t>(layer_.color.g) << 8) |
+      (static_cast<uint32_t>(layer_.color.b) << 16) |
+      (static_cast<uint32_t>(layer_.color.a) << 24);
+  ret = parcel->writeUint32(color);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.dataspace);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeInt32(layer_.transform);
+  if (ret != OK) return ret;
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size()));
+  if (ret != OK) return ret;
+
+  for (auto& rect: layer_.visible_regions) {
+    ret = parcel->writeInt32(rect.left);
+    ret = parcel->writeInt32(rect.top);
+    ret = parcel->writeInt32(rect.right);
+    ret = parcel->writeInt32(rect.bottom);
+    if (ret != OK) return ret;
+  }
+
+  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size()));
+  if (ret != OK) return ret;
+
+  for (auto& rect: layer_.damaged_regions) {
+    ret = parcel->writeInt32(rect.left);
+    ret = parcel->writeInt32(rect.top);
+    ret = parcel->writeInt32(rect.right);
+    ret = parcel->writeInt32(rect.bottom);
+    if (ret != OK) return ret;
+  }
+
+  return OK;
+}
+
+status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) {
+  status_t ret = parcel->readUint64(&layer_.id);
+  if (ret != OK) return ret;
+
+  layer_.buffer = new GraphicBuffer();
+  ret = parcel->read(*layer_.buffer);
+  if (ret != OK) {
+    layer_.buffer.clear();
+    return ret;
+  }
+
+  bool has_fence = 0;
+  ret = parcel->readBool(&has_fence);
+  if (ret != OK) return ret;
+
+  if (has_fence)
+    layer_.fence = new Fence(dup(parcel->readFileDescriptor()));
+  else
+    layer_.fence = new Fence();
+
+  ret = parcel->readInt32(&layer_.display_frame.left);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.display_frame.top);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.display_frame.right);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.display_frame.bottom);
+  if (ret != OK) return ret;
+
+  ret = parcel->readFloat(&layer_.crop.left);
+  if (ret != OK) return ret;
+
+  ret = parcel->readFloat(&layer_.crop.top);
+  if (ret != OK) return ret;
+
+  ret = parcel->readFloat(&layer_.crop.right);
+  if (ret != OK) return ret;
+
+  ret = parcel->readFloat(&layer_.crop.bottom);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode));
+  if (ret != OK) return ret;
+
+  ret = parcel->readFloat(&layer_.alpha);
+  if (ret != OK) return ret;
+
+  ret = parcel->readUint32(&layer_.type);
+  if (ret != OK) return ret;
+
+  ret = parcel->readUint32(&layer_.app_id);
+  if (ret != OK) return ret;
+
+  ret = parcel->readUint32(&layer_.z_order);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.cursor_x);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.cursor_y);
+  if (ret != OK) return ret;
+
+  uint32_t color;
+  ret = parcel->readUint32(&color);
+  if (ret != OK) return ret;
+  layer_.color.r = color & 0xFF;
+  layer_.color.g = (color >> 8) & 0xFF;
+  layer_.color.b = (color >> 16) & 0xFF;
+  layer_.color.a = (color >> 24) & 0xFF;
+
+  ret = parcel->readInt32(&layer_.dataspace);
+  if (ret != OK) return ret;
+
+  ret = parcel->readInt32(&layer_.transform);
+  if (ret != OK) return ret;
+
+  uint32_t size;
+  ret = parcel->readUint32(&size);
+  if (ret != OK) return ret;
+
+  for(size_t i = 0; i < size; i++) {
+    hwc_rect_t rect;
+    ret = parcel->readInt32(&rect.left);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.top);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.right);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.bottom);
+    if (ret != OK) return ret;
+
+    layer_.visible_regions.push_back(rect);
+  }
+
+  ret = parcel->readUint32(&size);
+  if (ret != OK) return ret;
+
+  for(size_t i = 0; i < size; i++) {
+    hwc_rect_t rect;
+    ret = parcel->readInt32(&rect.left);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.top);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.right);
+    if (ret != OK) return ret;
+
+    ret = parcel->readInt32(&rect.bottom);
+    if (ret != OK) return ret;
+
+    layer_.damaged_regions.push_back(rect);
+  }
+
+  return OK;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
new file mode 100644
index 0000000..4cf48f1
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
+
+#include <binder/Parcelable.h>
+#include <impl/vr_hwc.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+class ParcelableComposerLayer : public Parcelable {
+ public:
+  ParcelableComposerLayer();
+  ParcelableComposerLayer(const ComposerView::ComposerLayer& layer);
+  ~ParcelableComposerLayer() override;
+
+  ComposerView::ComposerLayer layer() const { return layer_; }
+
+  status_t writeToParcel(Parcel* parcel) const override;
+  status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+  ComposerView::ComposerLayer layer_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
new file mode 100644
index 0000000..9486f3c
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
@@ -0,0 +1,37 @@
+#include "android/dvr/parcelable_unique_fd.h"
+
+#include <binder/Parcel.h>
+
+namespace android {
+namespace dvr {
+
+ParcelableUniqueFd::ParcelableUniqueFd() {}
+
+ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence)
+    : fence_(dup(fence.get())) {}
+
+ParcelableUniqueFd::~ParcelableUniqueFd() {}
+
+status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const {
+  status_t ret = parcel->writeBool(fence_.get() >= 0);
+  if (ret != OK) return ret;
+
+  if (fence_.get() >= 0)
+    ret = parcel->writeUniqueFileDescriptor(fence_);
+
+  return ret;
+}
+
+status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) {
+  bool has_fence = 0;
+  status_t ret = parcel->readBool(&has_fence);
+  if (ret != OK) return ret;
+
+  if (has_fence)
+    ret = parcel->readUniqueFileDescriptor(&fence_);
+
+  return ret;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
new file mode 100644
index 0000000..daf9e6d
--- /dev/null
+++ b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace dvr {
+
+// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the
+// case where the FD is invalid (-1), unlike FileDescriptor which expects a
+// valid FD.
+class ParcelableUniqueFd : public Parcelable {
+ public:
+  ParcelableUniqueFd();
+  ParcelableUniqueFd(const base::unique_fd& fence);
+  ~ParcelableUniqueFd() override;
+
+  void set_fence(const base::unique_fd& fence) {
+    fence_.reset(dup(fence.get()));
+  }
+  base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); }
+
+  status_t writeToParcel(Parcel* parcel) const override;
+  status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+  base::unique_fd fence_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
new file mode 100644
index 0000000..ae54e56
--- /dev/null
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+#include <log/log.h>
+
+#include "impl/vr_hwc.h"
+#include "impl/vr_composer_client.h"
+
+namespace android {
+namespace dvr {
+
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::frameworks::vr::composer::V1_0::IVrComposerClient;
+
+VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
+    : ComposerClient(hal), mVrHal(hal) {}
+
+VrComposerClient::~VrComposerClient() {}
+
+std::unique_ptr<ComposerClient::CommandReader>
+VrComposerClient::createCommandReader() {
+  return std::unique_ptr<CommandReader>(new VrCommandReader(*this));
+}
+
+VrComposerClient::VrCommandReader::VrCommandReader(VrComposerClient& client)
+    : CommandReader(client), mVrClient(client), mVrHal(client.mVrHal) {}
+
+VrComposerClient::VrCommandReader::~VrCommandReader() {}
+
+bool VrComposerClient::VrCommandReader::parseCommand(
+    IComposerClient::Command command, uint16_t length) {
+  IVrComposerClient::VrCommand vrCommand =
+      static_cast<IVrComposerClient::VrCommand>(command);
+  switch (vrCommand) {
+    case IVrComposerClient::VrCommand::SET_LAYER_INFO:
+      return parseSetLayerInfo(length);
+    case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA:
+      return parseSetClientTargetMetadata(length);
+    case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA:
+      return parseSetLayerBufferMetadata(length);
+    default:
+      return CommandReader::parseCommand(command, length);
+  }
+}
+
+bool VrComposerClient::VrCommandReader::parseSetLayerInfo(uint16_t length) {
+  if (length != 2) {
+    return false;
+  }
+
+  auto err = mVrHal.setLayerInfo(mDisplay, mLayer, read(), read());
+  if (err != Error::NONE) {
+    mWriter.setError(getCommandLoc(), err);
+  }
+
+  return true;
+}
+
+bool VrComposerClient::VrCommandReader::parseSetClientTargetMetadata(
+    uint16_t length) {
+  if (length != 7)
+    return false;
+
+  auto err = mVrHal.setClientTargetMetadata(mDisplay, readBufferMetadata());
+  if (err != Error::NONE)
+    mWriter.setError(getCommandLoc(), err);
+
+  return true;
+}
+
+bool VrComposerClient::VrCommandReader::parseSetLayerBufferMetadata(
+    uint16_t length) {
+  if (length != 7)
+    return false;
+
+  auto err = mVrHal.setLayerBufferMetadata(mDisplay, mLayer,
+                                           readBufferMetadata());
+  if (err != Error::NONE)
+    mWriter.setError(getCommandLoc(), err);
+
+  return true;
+}
+
+IVrComposerClient::BufferMetadata
+VrComposerClient::VrCommandReader::readBufferMetadata() {
+  IVrComposerClient::BufferMetadata metadata = {
+    .width = read(),
+    .height = read(),
+    .stride = read(),
+    .layerCount = read(),
+    .format = static_cast<PixelFormat>(readSigned()),
+    .usage = read64(),
+  };
+  return metadata;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
new file mode 100644
index 0000000..1236be9
--- /dev/null
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
+
+#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <ComposerClient.h>
+#include <IComposerCommandBuffer.h>
+
+namespace android {
+namespace dvr {
+
+class VrHwc;
+
+using hardware::graphics::common::V1_0::PixelFormat;
+using hardware::graphics::composer::V2_1::implementation::ComposerClient;
+
+class VrComposerClient : public ComposerClient {
+ public:
+  VrComposerClient(android::dvr::VrHwc& hal);
+  virtual ~VrComposerClient();
+
+ private:
+  class VrCommandReader : public ComposerClient::CommandReader {
+   public:
+    VrCommandReader(VrComposerClient& client);
+    ~VrCommandReader() override;
+
+    bool parseCommand(IComposerClient::Command command,
+                      uint16_t length) override;
+
+   private:
+    bool parseSetLayerInfo(uint16_t length);
+    bool parseSetClientTargetMetadata(uint16_t length);
+    bool parseSetLayerBufferMetadata(uint16_t length);
+
+    IVrComposerClient::BufferMetadata readBufferMetadata();
+
+    VrComposerClient& mVrClient;
+    android::dvr::VrHwc& mVrHal;
+
+    VrCommandReader(const VrCommandReader&) = delete;
+    void operator=(const VrCommandReader&) = delete;
+  };
+
+  std::unique_ptr<CommandReader> createCommandReader() override;
+
+  dvr::VrHwc& mVrHal;
+
+  VrComposerClient(const VrComposerClient&) = delete;
+  void operator=(const VrComposerClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
new file mode 100644
index 0000000..5d51827
--- /dev/null
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -0,0 +1,863 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "impl/vr_hwc.h"
+
+#include <private/dvr/display_client.h>
+#include <ui/Fence.h>
+
+#include <mutex>
+
+#include "vr_composer_client.h"
+
+using namespace android::hardware::graphics::common::V1_0;
+using namespace android::hardware::graphics::composer::V2_1;
+
+using android::hardware::hidl_handle;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+namespace android {
+namespace dvr {
+namespace {
+
+using android::hardware::graphics::common::V1_0::PixelFormat;
+
+const Display kDefaultDisplayId = 1;
+const Config kDefaultConfigId = 1;
+
+sp<GraphicBuffer> CreateGraphicBuffer(
+    const native_handle_t* handle,
+    const IVrComposerClient::BufferMetadata& metadata) {
+   sp<GraphicBuffer> buffer = new GraphicBuffer(
+      handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height,
+      static_cast<int32_t>(metadata.format), metadata.layerCount,
+      metadata.usage, metadata.stride);
+   if (buffer->initCheck() != OK) {
+     ALOGE("Failed to create graphic buffer");
+     return nullptr;
+   }
+
+   return buffer;
+}
+
+void GetPrimaryDisplaySize(int32_t* width, int32_t* height) {
+  *width = 1080;
+  *height = 1920;
+
+  int error = 0;
+  auto display_client = display::DisplayClient::Create(&error);
+  if (!display_client) {
+    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+          error);
+    return;
+  }
+
+  auto status = display_client->GetDisplayMetrics();
+  if (!status) {
+    ALOGE("Could not get display metrics from display service : %s(%d)",
+          status.GetErrorMessage().c_str(), status.error());
+    return;
+  }
+
+  *width = status.get().display_width;
+  *height = status.get().display_height;
+}
+
+}  // namespace
+
+HwcDisplay::HwcDisplay(int32_t width, int32_t height)
+    : width_(width), height_(height) {}
+
+HwcDisplay::~HwcDisplay() {}
+
+bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
+                                 base::unique_fd fence) {
+  if (handle)
+    buffer_ = CreateGraphicBuffer(handle, buffer_metadata_);
+
+  fence_ = new Fence(fence.release());
+  return true;
+}
+
+void HwcDisplay::SetClientTargetMetadata(
+    const IVrComposerClient::BufferMetadata& metadata) {
+  buffer_metadata_ = metadata;
+}
+
+HwcLayer* HwcDisplay::CreateLayer() {
+  uint64_t layer_id = layer_ids_++;
+  layers_.push_back(HwcLayer(layer_id));
+  return &layers_.back();
+}
+
+HwcLayer* HwcDisplay::GetLayer(Layer id) {
+  for (size_t i = 0; i < layers_.size(); ++i)
+    if (layers_[i].info.id == id)
+      return &layers_[i];
+
+  return nullptr;
+}
+
+bool HwcDisplay::DestroyLayer(Layer id) {
+  for (auto it = layers_.begin(); it != layers_.end(); ++it) {
+    if (it->info.id == id) {
+      layers_.erase(it);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void HwcDisplay::GetChangedCompositionTypes(
+    std::vector<Layer>* layer_ids,
+    std::vector<IComposerClient::Composition>* types) {
+  std::sort(layers_.begin(), layers_.end(),
+            [](const auto& lhs, const auto& rhs) {
+              return lhs.info.z_order < rhs.info.z_order;
+            });
+
+  int first_client_layer = -1, last_client_layer = -1;
+  for (size_t i = 0; i < layers_.size(); ++i) {
+    switch (layers_[i].composition_type) {
+      case IComposerClient::Composition::SOLID_COLOR:
+      case IComposerClient::Composition::CURSOR:
+      case IComposerClient::Composition::SIDEBAND:
+        if (first_client_layer < 0)
+          first_client_layer = i;
+
+        last_client_layer = i;
+        break;
+      default:
+        break;
+    }
+  }
+
+  for (size_t i = 0; i < layers_.size(); ++i) {
+    if (i >= first_client_layer && i <= last_client_layer) {
+      if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
+        layer_ids->push_back(layers_[i].info.id);
+        types->push_back(IComposerClient::Composition::CLIENT);
+        layers_[i].composition_type = IComposerClient::Composition::CLIENT;
+      }
+
+      continue;
+    }
+
+    if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
+      layer_ids->push_back(layers_[i].info.id);
+      types->push_back(IComposerClient::Composition::DEVICE);
+      layers_[i].composition_type = IComposerClient::Composition::DEVICE;
+    }
+  }
+}
+
+Error HwcDisplay::GetFrame(
+    std::vector<ComposerView::ComposerLayer>* out_frames) {
+  bool queued_client_target = false;
+  std::vector<ComposerView::ComposerLayer> frame;
+  for (const auto& layer : layers_) {
+    if (layer.composition_type == IComposerClient::Composition::CLIENT) {
+      if (queued_client_target)
+        continue;
+
+      if (!buffer_.get()) {
+        ALOGE("Client composition requested but no client target buffer");
+        return Error::BAD_LAYER;
+      }
+
+      ComposerView::ComposerLayer client_target_layer = {
+          .buffer = buffer_,
+          .fence = fence_.get() ? fence_ : new Fence(-1),
+          .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
+            static_cast<int32_t>(buffer_->getHeight())},
+          .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
+            static_cast<float>(buffer_->getHeight())},
+          .blend_mode = IComposerClient::BlendMode::NONE,
+      };
+
+      frame.push_back(client_target_layer);
+      queued_client_target = true;
+    } else {
+      if (!layer.info.buffer.get() || !layer.info.fence.get()) {
+        ALOGV("Layer requested without valid buffer");
+        continue;
+      }
+
+      frame.push_back(layer.info);
+    }
+  }
+
+  out_frames->swap(frame);
+  return Error::NONE;
+}
+
+std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
+  std::vector<Layer> last_frame_layers;
+  last_frame_layers.swap(last_frame_layers_ids_);
+
+  for (const auto& layer : layers_)
+    last_frame_layers_ids_.push_back(layer.info.id);
+
+  return last_frame_layers;
+}
+
+void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) {
+  color_transform_hint_ = hint;
+  if (matrix)
+    memcpy(color_transform_, matrix, sizeof(color_transform_));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VrHwcClient
+
+VrHwc::VrHwc() {}
+
+VrHwc::~VrHwc() {}
+
+bool VrHwc::hasCapability(Capability capability) const { return false; }
+
+void VrHwc::removeClient() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  client_ = nullptr;
+}
+
+void VrHwc::enableCallback(bool enable) {
+  if (enable && client_ != nullptr) {
+    {
+      int32_t width, height;
+      GetPrimaryDisplaySize(&width, &height);
+      std::lock_guard<std::mutex> guard(mutex_);
+      // Create the primary display late to avoid initialization issues between
+      // VR HWC and SurfaceFlinger.
+      displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
+    }
+    client_.promote()->onHotplug(kDefaultDisplayId,
+                                 IComposerCallback::Connection::CONNECTED);
+  }
+}
+
+uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
+
+Error VrHwc::createVirtualDisplay(uint32_t width, uint32_t height,
+                                  PixelFormat* format, Display* outDisplay) {
+  *format = PixelFormat::RGBA_8888;
+  *outDisplay = display_count_;
+  displays_[display_count_].reset(new HwcDisplay(width, height));
+  display_count_++;
+  return Error::NONE;
+}
+
+Error VrHwc::destroyVirtualDisplay(Display display) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (display == kDefaultDisplayId || displays_.erase(display) == 0)
+    return Error::BAD_DISPLAY;
+  ComposerView::Frame frame;
+  frame.display_id = display;
+  frame.removed = true;
+  if (observer_)
+    observer_->OnNewFrame(frame);
+  return Error::NONE;
+}
+
+Error VrHwc::createLayer(Display display, Layer* outLayer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* layer = display_ptr->CreateLayer();
+  *outLayer = layer->info.id;
+  return Error::NONE;
+}
+
+Error VrHwc::destroyLayer(Display display, Layer layer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
+    return Error::BAD_DISPLAY;
+  }
+
+  return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
+}
+
+Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  *outConfig = kDefaultConfigId;
+  return Error::NONE;
+}
+
+Error VrHwc::getClientTargetSupport(Display display, uint32_t width,
+                                    uint32_t height, PixelFormat format,
+                                    Dataspace dataspace) {
+  return Error::NONE;
+}
+
+Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) {
+  std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
+  *outModes = hidl_vec<ColorMode>(color_modes);
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayAttribute(Display display, Config config,
+                                 IComposerClient::Attribute attribute,
+                                 int32_t* outValue) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
+    return Error::BAD_DISPLAY;
+  }
+  if (config != kDefaultConfigId) {
+    return Error::BAD_CONFIG;
+  }
+
+  switch (attribute) {
+    case IComposerClient::Attribute::WIDTH:
+      *outValue = display_ptr->width();
+      break;
+    case IComposerClient::Attribute::HEIGHT:
+      *outValue = display_ptr->height();
+      break;
+    case IComposerClient::Attribute::VSYNC_PERIOD:
+      *outValue = 1000 * 1000 * 1000 / 30;  // 30fps
+      break;
+    case IComposerClient::Attribute::DPI_X:
+    case IComposerClient::Attribute::DPI_Y:
+      *outValue = 300 * 1000;  // 300dpi
+      break;
+    default:
+      return Error::BAD_PARAMETER;
+  }
+
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  std::vector<Config> configs(1, kDefaultConfigId);
+  *outConfigs = hidl_vec<Config>(configs);
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayName(Display display, hidl_string* outName) {
+  *outName = hidl_string();
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayType(Display display,
+                            IComposerClient::DisplayType* outType) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
+    *outType = IComposerClient::DisplayType::INVALID;
+    return Error::BAD_DISPLAY;
+  }
+
+  if (display == kDefaultDisplayId)
+    *outType = IComposerClient::DisplayType::PHYSICAL;
+  else
+    *outType = IComposerClient::DisplayType::VIRTUAL;
+
+  return Error::NONE;
+}
+
+Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
+  *outSupport = false;
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  return Error::NONE;
+}
+
+Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+                                float* outMaxLuminance,
+                                float* outMaxAverageLuminance,
+                                float* outMinLuminance) {
+  *outMaxLuminance = 0;
+  *outMaxAverageLuminance = 0;
+  *outMinLuminance = 0;
+  return Error::NONE;
+}
+
+Error VrHwc::setActiveConfig(Display display, Config config) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+  if (config != kDefaultConfigId)
+    return Error::BAD_CONFIG;
+
+  display_ptr->set_active_config(config);
+  return Error::NONE;
+}
+
+Error VrHwc::setColorMode(Display display, ColorMode mode) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->set_color_mode(mode);
+  return Error::NONE;
+}
+
+Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->set_power_mode(mode);
+  return Error::NONE;
+}
+
+Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->set_vsync_enabled(enabled);
+  return Error::NONE;
+}
+
+Error VrHwc::setColorTransform(Display display, const float* matrix,
+                               int32_t hint) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->SetColorTransform(matrix, hint);
+  return Error::NONE;
+}
+
+Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
+                             int32_t acquireFence, int32_t dataspace,
+                             const std::vector<hwc_rect_t>& damage) {
+  base::unique_fd fence(acquireFence);
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  if (target == nullptr)
+    return Error::NONE;
+
+  if (!display_ptr->SetClientTarget(target, std::move(fence)))
+    return Error::BAD_PARAMETER;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
+                             int32_t releaseFence) {
+  base::unique_fd fence(releaseFence);
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  // TODO(dnicoara): Is it necessary to do anything here?
+  return Error::NONE;
+}
+
+Error VrHwc::validateDisplay(
+    Display display, std::vector<Layer>* outChangedLayers,
+    std::vector<IComposerClient::Composition>* outCompositionTypes,
+    uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+    std::vector<uint32_t>* outRequestMasks) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->GetChangedCompositionTypes(outChangedLayers,
+                                          outCompositionTypes);
+  return Error::NONE;
+}
+
+Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; }
+
+Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
+                            std::vector<Layer>* outLayers,
+                            std::vector<int32_t>* outReleaseFences) {
+  *outPresentFence = -1;
+  outLayers->clear();
+  outReleaseFences->clear();
+
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  ComposerView::Frame frame;
+  std::vector<Layer> last_frame_layers;
+  Error status = display_ptr->GetFrame(&frame.layers);
+  frame.display_id = display;
+  frame.display_width = display_ptr->width();
+  frame.display_height = display_ptr->height();
+  frame.active_config = display_ptr->active_config();
+  frame.power_mode = display_ptr->power_mode();
+  frame.vsync_enabled = display_ptr->vsync_enabled();
+  frame.color_transform_hint = display_ptr->color_transform_hint();
+  frame.color_mode = display_ptr->color_mode();
+  memcpy(frame.color_transform, display_ptr->color_transform(),
+         sizeof(frame.color_transform));
+  if (status != Error::NONE)
+    return status;
+
+  last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
+
+  base::unique_fd fence;
+  if (observer_)
+    fence = observer_->OnNewFrame(frame);
+
+  if (fence.get() < 0)
+    return Error::NONE;
+
+  *outPresentFence = dup(fence.get());
+  outLayers->swap(last_frame_layers);
+  for (size_t i = 0; i < outLayers->size(); ++i)
+    outReleaseFences->push_back(dup(fence.get()));
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
+                                    int32_t y) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.cursor_x = x;
+  hwc_layer->info.cursor_y = y;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerBuffer(Display display, Layer layer,
+                            buffer_handle_t buffer, int32_t acquireFence) {
+  base::unique_fd fence(acquireFence);
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.buffer = CreateGraphicBuffer(
+      buffer, hwc_layer->buffer_metadata);
+  hwc_layer->info.fence = new Fence(fence.release());
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
+                                   const std::vector<hwc_rect_t>& damage) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.damaged_regions = damage;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.blend_mode =
+      static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerColor(Display display, Layer layer,
+                           IComposerClient::Color color) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.color = color;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerCompositionType(Display display, Layer layer,
+                                     int32_t type) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerDataspace(Display display, Layer layer,
+                               int32_t dataspace) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.dataspace = dataspace;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
+                                  const hwc_rect_t& frame) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.display_frame =
+      {frame.left, frame.top, frame.right, frame.bottom};
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.alpha = alpha;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
+                                    buffer_handle_t stream) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
+                                const hwc_frect_t& crop) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerTransform(Display display, Layer layer,
+                               int32_t transform) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.transform = transform;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
+                                   const std::vector<hwc_rect_t>& visible) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.visible_regions = visible;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.z_order = z;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
+                          uint32_t appId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->info.type = type;
+  hwc_layer->info.app_id = appId;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setClientTargetMetadata(
+    Display display, const IVrComposerClient::BufferMetadata& metadata) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  display_ptr->SetClientTargetMetadata(metadata);
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerBufferMetadata(
+    Display display, Layer layer,
+    const IVrComposerClient::BufferMetadata& metadata) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
+
+  hwc_layer->buffer_metadata = metadata;
+
+  return Error::NONE;
+}
+
+Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) {
+  hidl_cb(hidl_vec<Capability>());
+  return Void();
+}
+
+Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+  hidl_cb(hidl_string());
+  return Void();
+}
+
+Return<void> VrHwc::createClient(createClient_cb hidl_cb) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  Error status = Error::NONE;
+  sp<VrComposerClient> client;
+  if (client_ == nullptr) {
+    client = new VrComposerClient(*this);
+    client->initialize();
+  } else {
+    ALOGE("Already have a client");
+    status = Error::NO_RESOURCES;
+  }
+
+  client_ = client;
+  hidl_cb(status, client);
+  return Void();
+}
+
+void VrHwc::RegisterObserver(Observer* observer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (observer_)
+    ALOGE("Overwriting observer");
+  else
+    observer_ = observer;
+}
+
+void VrHwc::UnregisterObserver(Observer* observer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (observer != observer_)
+    ALOGE("Trying to unregister unknown observer");
+  else
+    observer_ = nullptr;
+}
+
+HwcDisplay* VrHwc::FindDisplay(Display display) {
+  auto iter = displays_.find(display);
+  return iter == displays_.end() ? nullptr : iter->second.get();
+}
+
+ComposerView* GetComposerViewFromIComposer(
+    hardware::graphics::composer::V2_1::IComposer* composer) {
+  return static_cast<VrHwc*>(composer);
+}
+
+IComposer* HIDL_FETCH_IComposer(const char*) { return new VrHwc(); }
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
new file mode 100644
index 0000000..df04208
--- /dev/null
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
+
+#include <android-base/unique_fd.h>
+#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <ComposerBase.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+#include <mutex>
+#include <unordered_map>
+
+using namespace android::frameworks::vr::composer::V1_0;
+using namespace android::hardware::graphics::common::V1_0;
+using namespace android::hardware::graphics::composer::V2_1;
+
+using android::hardware::hidl_handle;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+namespace android {
+
+class Fence;
+
+namespace dvr {
+
+class VrComposerClient;
+
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::composer::V2_1::implementation::ComposerBase;
+
+class ComposerView {
+ public:
+  struct ComposerLayer {
+    using Recti = hardware::graphics::composer::V2_1::IComposerClient::Rect;
+    using Rectf = hardware::graphics::composer::V2_1::IComposerClient::FRect;
+    using BlendMode =
+        hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+
+    Layer id;
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+    Recti display_frame;
+    Rectf crop;
+    BlendMode blend_mode;
+    float alpha;
+    uint32_t type;
+    uint32_t app_id;
+    uint32_t z_order;
+    int32_t cursor_x;
+    int32_t cursor_y;
+    IComposerClient::Color color;
+    int32_t dataspace;
+    int32_t transform;
+    std::vector<hwc_rect_t> visible_regions;
+    std::vector<hwc_rect_t> damaged_regions;
+  };
+
+  struct Frame {
+    Display display_id;
+    // This is set to true to notify the upper layer that the display is
+    // being removed, or left false in the case of a normal frame. The upper
+    // layer tracks display IDs and will handle new ones showing up.
+    bool removed = false;
+    int32_t display_width;
+    int32_t display_height;
+    Config active_config;
+    ColorMode color_mode;
+    IComposerClient::PowerMode power_mode;
+    IComposerClient::Vsync vsync_enabled;
+    float color_transform[16];
+    int32_t color_transform_hint;
+    std::vector<ComposerLayer> layers;
+  };
+
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    // Returns a list of layers that need to be shown together. Layers are
+    // returned in z-order, with the lowest layer first.
+    virtual base::unique_fd OnNewFrame(const Frame& frame) = 0;
+  };
+
+  virtual ~ComposerView() {}
+
+  virtual void RegisterObserver(Observer* observer) = 0;
+  virtual void UnregisterObserver(Observer* observer) = 0;
+};
+
+struct HwcLayer {
+  using Composition =
+      hardware::graphics::composer::V2_1::IComposerClient::Composition;
+
+  HwcLayer(Layer new_id) {
+    info.id = new_id;
+  }
+
+  Composition composition_type;
+  ComposerView::ComposerLayer info;
+  IVrComposerClient::BufferMetadata buffer_metadata;
+};
+
+class HwcDisplay {
+ public:
+  HwcDisplay(int32_t width, int32_t height);
+  ~HwcDisplay();
+
+  int32_t width() const { return width_; }
+  int32_t height() const { return height_; }
+
+  HwcLayer* CreateLayer();
+  bool DestroyLayer(Layer id);
+  HwcLayer* GetLayer(Layer id);
+
+  bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
+  void SetClientTargetMetadata(
+      const IVrComposerClient::BufferMetadata& metadata);
+
+  void GetChangedCompositionTypes(
+      std::vector<Layer>* layer_ids,
+      std::vector<IComposerClient::Composition>* composition);
+
+  Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
+
+  std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
+
+  Config active_config() const { return active_config_; }
+  void set_active_config(Config config) { active_config_ = config; }
+
+  ColorMode color_mode() const { return color_mode_; }
+  void set_color_mode(ColorMode mode) { color_mode_ = mode; }
+
+  IComposerClient::PowerMode power_mode() const { return power_mode_; }
+  void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
+
+  IComposerClient::Vsync vsync_enabled() const { return vsync_enabled_; }
+  void set_vsync_enabled(IComposerClient::Vsync vsync) {
+    vsync_enabled_ = vsync;
+  }
+
+  const float* color_transform() const { return color_transform_; }
+  int32_t color_transform_hint() const { return color_transform_hint_; }
+  void SetColorTransform(const float* matrix, int32_t hint);
+
+ private:
+  // The client target buffer and the associated fence.
+  sp<GraphicBuffer> buffer_;
+  IVrComposerClient::BufferMetadata buffer_metadata_;
+  sp<Fence> fence_;
+
+  // List of currently active layers.
+  std::vector<HwcLayer> layers_;
+
+  std::vector<Layer> last_frame_layers_ids_;
+
+  // Layer ID generator.
+  uint64_t layer_ids_ = 1;
+
+  int32_t width_;
+  int32_t height_;
+
+  Config active_config_;
+  ColorMode color_mode_;
+  IComposerClient::PowerMode power_mode_;
+  IComposerClient::Vsync vsync_enabled_;
+  float color_transform_[16];
+  int32_t color_transform_hint_;
+
+  HwcDisplay(const HwcDisplay&) = delete;
+  void operator=(const HwcDisplay&) = delete;
+};
+
+class VrHwc : public IComposer, public ComposerBase, public ComposerView {
+ public:
+  VrHwc();
+  ~VrHwc() override;
+
+  bool hasCapability(Capability capability) const;
+
+  Error setLayerInfo(Display display, Layer layer, uint32_t type,
+                     uint32_t appId);
+  Error setClientTargetMetadata(
+      Display display, const IVrComposerClient::BufferMetadata& metadata);
+  Error setLayerBufferMetadata(
+      Display display, Layer layer,
+      const IVrComposerClient::BufferMetadata& metadata);
+
+  // ComposerBase
+  void removeClient() override;
+  void enableCallback(bool enable) override;
+
+  uint32_t getMaxVirtualDisplayCount() override;
+  Error createVirtualDisplay(uint32_t width, uint32_t height,
+      PixelFormat* format, Display* outDisplay) override;
+  Error destroyVirtualDisplay(Display display) override;
+
+  Error createLayer(Display display, Layer* outLayer) override;
+  Error destroyLayer(Display display, Layer layer) override;
+
+  Error getActiveConfig(Display display, Config* outConfig) override;
+  Error getClientTargetSupport(Display display,
+          uint32_t width, uint32_t height,
+          PixelFormat format, Dataspace dataspace) override;
+  Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+  Error getDisplayAttribute(Display display, Config config,
+          IComposerClient::Attribute attribute, int32_t* outValue) override;
+  Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+  Error getDisplayName(Display display, hidl_string* outName) override;
+  Error getDisplayType(Display display,
+          IComposerClient::DisplayType* outType) override;
+  Error getDozeSupport(Display display, bool* outSupport) override;
+  Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+          float* outMaxLuminance, float* outMaxAverageLuminance,
+          float* outMinLuminance) override;
+
+  Error setActiveConfig(Display display, Config config) override;
+  Error setColorMode(Display display, ColorMode mode) override;
+  Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+  Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+  Error setColorTransform(Display display, const float* matrix,
+          int32_t hint) override;
+  Error setClientTarget(Display display, buffer_handle_t target,
+          int32_t acquireFence, int32_t dataspace,
+          const std::vector<hwc_rect_t>& damage) override;
+  Error setOutputBuffer(Display display, buffer_handle_t buffer,
+          int32_t releaseFence) override;
+  Error validateDisplay(Display display,
+          std::vector<Layer>* outChangedLayers,
+          std::vector<IComposerClient::Composition>* outCompositionTypes,
+          uint32_t* outDisplayRequestMask,
+          std::vector<Layer>* outRequestedLayers,
+          std::vector<uint32_t>* outRequestMasks) override;
+  Error acceptDisplayChanges(Display display) override;
+  Error presentDisplay(Display display, int32_t* outPresentFence,
+          std::vector<Layer>* outLayers,
+          std::vector<int32_t>* outReleaseFences) override;
+
+  Error setLayerCursorPosition(Display display, Layer layer,
+          int32_t x, int32_t y) override;
+  Error setLayerBuffer(Display display, Layer layer,
+          buffer_handle_t buffer, int32_t acquireFence) override;
+  Error setLayerSurfaceDamage(Display display, Layer layer,
+          const std::vector<hwc_rect_t>& damage) override;
+  Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+  Error setLayerColor(Display display, Layer layer,
+          IComposerClient::Color color) override;
+  Error setLayerCompositionType(Display display, Layer layer,
+          int32_t type) override;
+  Error setLayerDataspace(Display display, Layer layer,
+          int32_t dataspace) override;
+  Error setLayerDisplayFrame(Display display, Layer layer,
+          const hwc_rect_t& frame) override;
+  Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+  Error setLayerSidebandStream(Display display, Layer layer,
+          buffer_handle_t stream) override;
+  Error setLayerSourceCrop(Display display, Layer layer,
+          const hwc_frect_t& crop) override;
+  Error setLayerTransform(Display display, Layer layer,
+          int32_t transform) override;
+  Error setLayerVisibleRegion(Display display, Layer layer,
+          const std::vector<hwc_rect_t>& visible) override;
+  Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+  // IComposer:
+  Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+  Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+  Return<void> createClient(createClient_cb hidl_cb) override;
+
+  // ComposerView:
+  void RegisterObserver(Observer* observer) override;
+  void UnregisterObserver(Observer* observer) override;
+
+ private:
+  HwcDisplay* FindDisplay(Display display);
+
+  wp<VrComposerClient> client_;
+  sp<IComposerCallback> callbacks_;
+
+  // Guard access to internal state from binder threads.
+  std::mutex mutex_;
+
+  std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
+  Display display_count_ = 2;
+
+  Observer* observer_ = nullptr;
+
+  VrHwc(const VrHwc&) = delete;
+  void operator=(const VrHwc&) = delete;
+};
+
+
+ComposerView* GetComposerViewFromIComposer(
+    hardware::graphics::composer::V2_1::IComposer* composer);
+
+hardware::graphics::composer::V2_1::IComposer* HIDL_FETCH_IComposer(
+    const char* name);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp
new file mode 100644
index 0000000..d082f4b
--- /dev/null
+++ b/services/vr/hardware_composer/tests/vr_composer_test.cpp
@@ -0,0 +1,151 @@
+#include <android/dvr/BnVrComposerCallback.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <sys/eventfd.h>
+#include <vr_composer.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+const char kVrDisplayName[] = "VrDisplay_Test";
+
+class TestComposerCallback : public BnVrComposerCallback {
+ public:
+  TestComposerCallback() {}
+  ~TestComposerCallback() override = default;
+
+  ComposerView::Frame last_frame() const { return last_frame_; }
+
+  binder::Status onNewFrame(
+      const ParcelableComposerFrame& frame,
+      ParcelableUniqueFd* /* fence */) override {
+    last_frame_ = frame.frame();
+    return binder::Status::ok();
+  }
+
+ private:
+  ComposerView::Frame last_frame_;
+
+  TestComposerCallback(const TestComposerCallback&) = delete;
+  void operator=(const TestComposerCallback&) = delete;
+};
+
+class TestComposerCallbackWithFence : public TestComposerCallback {
+ public:
+  ~TestComposerCallbackWithFence() override = default;
+
+  binder::Status onNewFrame(
+      const ParcelableComposerFrame& frame,
+      ParcelableUniqueFd* fence) override {
+    binder::Status status = TestComposerCallback::onNewFrame(frame, fence);
+
+    base::unique_fd fd(eventfd(0, 0));
+    EXPECT_LE(0, fd.get());
+    fence->set_fence(fd);
+
+    return status;
+  }
+};
+
+sp<GraphicBuffer> CreateBuffer() {
+  return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888,
+                           GraphicBuffer::USAGE_HW_TEXTURE);
+}
+
+}  // namespace
+
+class VrComposerTest : public testing::Test {
+ public:
+  VrComposerTest() : composer_(new VrComposer()) {}
+  ~VrComposerTest() override = default;
+
+  sp<IVrComposer> GetComposerProxy() const {
+    sp<IServiceManager> sm(defaultServiceManager());
+    return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName)));
+  }
+
+  void SetUp() override {
+    sp<IServiceManager> sm(defaultServiceManager());
+    EXPECT_EQ(OK,
+              sm->addService(String16(kVrDisplayName), composer_, false));
+  }
+
+ protected:
+  sp<VrComposer> composer_;
+
+  VrComposerTest(const VrComposerTest&) = delete;
+  void operator=(const VrComposerTest&) = delete;
+};
+
+TEST_F(VrComposerTest, TestWithoutObserver) {
+  sp<IVrComposer> composer = GetComposerProxy();
+  ComposerView::Frame frame;
+
+  base::unique_fd fence = composer_->OnNewFrame(frame);
+  ASSERT_EQ(-1, fence.get());
+}
+
+TEST_F(VrComposerTest, TestWithObserver) {
+  sp<IVrComposer> composer = GetComposerProxy();
+  sp<TestComposerCallback> callback = new TestComposerCallback();
+  ASSERT_TRUE(composer->registerObserver(callback).isOk());
+
+  ComposerView::Frame frame;
+  base::unique_fd fence = composer_->OnNewFrame(frame);
+  ASSERT_EQ(-1, fence.get());
+}
+
+TEST_F(VrComposerTest, TestWithOneLayer) {
+  sp<IVrComposer> composer = GetComposerProxy();
+  sp<TestComposerCallback> callback = new TestComposerCallbackWithFence();
+  ASSERT_TRUE(composer->registerObserver(callback).isOk());
+
+  ComposerView::Frame frame;
+  frame.display_id = 1;
+  frame.removed = false;
+  frame.display_width = 600;
+  frame.display_height = 400;
+  frame.layers.push_back(ComposerView::ComposerLayer{
+    .id = 1,
+    .buffer = CreateBuffer(),
+    .fence = new Fence(eventfd(0, 0)),
+    .display_frame = {0, 0, 600, 400},
+    .crop = {0.0f, 0.0f, 600.0f, 400.0f},
+    .blend_mode = IComposerClient::BlendMode::NONE,
+    .alpha = 1.0f,
+    .type = 1,
+    .app_id = 1,
+  });
+  base::unique_fd fence = composer_->OnNewFrame(frame);
+  ASSERT_LE(0, fence.get());
+
+  ComposerView::Frame received_frame = callback->last_frame();
+  ASSERT_EQ(frame.display_id, received_frame.display_id);
+  ASSERT_EQ(frame.display_width, received_frame.display_width);
+  ASSERT_EQ(frame.display_height, received_frame.display_height);
+  ASSERT_EQ(frame.removed, received_frame.removed);
+  ASSERT_EQ(1u, received_frame.layers.size());
+  ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id);
+  ASSERT_NE(nullptr, received_frame.layers[0].buffer.get());
+  ASSERT_TRUE(received_frame.layers[0].fence->isValid());
+  ASSERT_EQ(frame.layers[0].display_frame.left,
+            received_frame.layers[0].display_frame.left);
+  ASSERT_EQ(frame.layers[0].display_frame.top,
+            received_frame.layers[0].display_frame.top);
+  ASSERT_EQ(frame.layers[0].display_frame.right,
+            received_frame.layers[0].display_frame.right);
+  ASSERT_EQ(frame.layers[0].display_frame.bottom,
+            received_frame.layers[0].display_frame.bottom);
+  ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left);
+  ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top);
+  ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right);
+  ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom);
+  ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode);
+  ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha);
+  ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type);
+  ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp
new file mode 100644
index 0000000..c45fbf4
--- /dev/null
+++ b/services/vr/hardware_composer/vr_composer.cpp
@@ -0,0 +1,67 @@
+#include "vr_composer.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+bool CheckPermission() {
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
+  const bool permission = PermissionCache::checkPermission(
+      String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid);
+  if (!permission)
+    ALOGE("permission denied to pid=%d uid=%u", pid, uid);
+
+  return permission;
+}
+
+}  // namespace
+
+VrComposer::VrComposer() {}
+
+VrComposer::~VrComposer() {}
+
+binder::Status VrComposer::registerObserver(
+    const sp<IVrComposerCallback>& callback) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  if (!CheckPermission())
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+
+  if (callback_.get()) {
+    ALOGE("Failed to register callback, already registered");
+    return binder::Status::fromStatusT(ALREADY_EXISTS);
+  }
+
+  callback_ = callback;
+  IInterface::asBinder(callback_)->linkToDeath(this);
+  return binder::Status::ok();
+}
+
+base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  if (!callback_.get())
+    return base::unique_fd();
+
+  ParcelableComposerFrame parcelable_frame(frame);
+  ParcelableUniqueFd fence;
+  binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence);
+  if (!ret.isOk())
+    ALOGE("Failed to send new frame: %s", ret.toString8().string());
+
+  return fence.fence();
+}
+
+void VrComposer::binderDied(const wp<IBinder>& /* who */) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  callback_ = nullptr;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h
new file mode 100644
index 0000000..93d1f2b
--- /dev/null
+++ b/services/vr/hardware_composer/vr_composer.h
@@ -0,0 +1,48 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
+
+#include <android/dvr/BnVrComposer.h>
+#include <impl/vr_hwc.h>
+
+namespace android {
+namespace dvr {
+
+class VrComposerCallback;
+
+// Implementation of the IVrComposer service used to notify VR Window Manager
+// when SurfaceFlinger presents 2D UI changes.
+//
+// VR HWC updates the presented frame via the ComposerView::Observer interface.
+// On notification |callback_| is called to update VR Window Manager.
+// NOTE: If VR Window Manager isn't connected, the notification is a no-op.
+class VrComposer
+    : public BnVrComposer,
+      public ComposerView::Observer,
+      public IBinder::DeathRecipient {
+ public:
+  VrComposer();
+  ~VrComposer() override;
+
+  // BnVrComposer:
+  binder::Status registerObserver(
+      const sp<IVrComposerCallback>& callback) override;
+
+  // ComposerView::Observer:
+  base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
+
+ private:
+  // IBinder::DeathRecipient:
+  void binderDied(const wp<IBinder>& who) override;
+
+  std::mutex mutex_;
+
+  sp<IVrComposerCallback> callback_;
+
+  VrComposer(const VrComposer&) = delete;
+  void operator=(const VrComposer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  //  ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
new file mode 100644
index 0000000..f980220
--- /dev/null
+++ b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <hwbinder/IPCThreadState.h>
+#include <impl/vr_hwc.h>
+#include <inttypes.h>
+
+#include "vr_composer.h"
+
+int main() {
+  android::ProcessState::self()->startThreadPool();
+
+  // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR
+  // mode.
+  const char instance[] = "vr";
+  android::sp<IComposer> service =
+      android::dvr::HIDL_FETCH_IComposer(instance);
+
+  LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
+  LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
+
+  LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK,
+                      "Failed to register service");
+
+  android::sp<android::dvr::VrComposer> composer =
+      new android::dvr::VrComposer();
+
+  android::dvr::ComposerView* composer_view =
+      android::dvr::GetComposerViewFromIComposer(service.get());
+  composer_view->RegisterObserver(composer.get());
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+
+  // Register the binder service used by VR Window Manager service to receive
+  // frame information from VR HWC HAL.
+  android::status_t status = sm->addService(
+      android::dvr::VrComposer::SERVICE_NAME(), composer.get(),
+      false /* allowIsolated */);
+  LOG_ALWAYS_FATAL_IF(status != android::OK,
+                      "VrDisplay service failed to start: %" PRId32, status);
+
+  android::hardware::ProcessState::self()->startThreadPool();
+  android::hardware::IPCThreadState::self()->joinThreadPool();
+
+  composer_view->UnregisterObserver(composer.get());
+
+  return 0;
+}
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
new file mode 100644
index 0000000..52d4da8
--- /dev/null
+++ b/services/vr/hardware_composer/vr_hwc.rc
@@ -0,0 +1,5 @@
+service vr_hwc /system/bin/vr_hwc
+  class hal
+  user system
+  group system graphics
+  onrestart restart surfaceflinger
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
new file mode 100644
index 0000000..6256e90
--- /dev/null
+++ b/services/vr/performanced/Android.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	cpu_set.cpp \
+	main.cpp \
+	performance_service.cpp \
+	task.cpp
+
+staticLibraries := \
+	libperformance \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"performanced\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := performanced
+LOCAL_INIT_RC := performanced.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := performance_service_tests.cpp
+LOCAL_STATIC_LIBRARIES := $(staticLibraries) libgtest_main
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := performance_service_tests
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
diff --git a/services/vr/performanced/CPPLINT.cfg b/services/vr/performanced/CPPLINT.cfg
new file mode 100644
index 0000000..fd379da
--- /dev/null
+++ b/services/vr/performanced/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-runtime/int
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
new file mode 100644
index 0000000..1a3723f
--- /dev/null
+++ b/services/vr/performanced/cpu_set.cpp
@@ -0,0 +1,287 @@
+#include "cpu_set.h"
+
+#include <log/log.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+
+#include "directory_reader.h"
+#include "stdio_filebuf.h"
+#include "task.h"
+#include "unique_file.h"
+
+namespace {
+
+constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
+constexpr pid_t kKernelThreadDaemonPid = 2;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+bool CpuSet::prefix_enabled_ = false;
+
+void CpuSetManager::Load(const std::string& cpuset_root) {
+  if (!root_set_)
+    root_set_ = Create(cpuset_root);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(const std::string& path) {
+  base::unique_fd root_cpuset_fd(open(path.c_str(), kDirectoryFlags));
+  if (root_cpuset_fd.get() < 0) {
+    ALOGE("CpuSet::Create: Failed to open \"%s\": %s", path.c_str(),
+          strerror(errno));
+    return nullptr;
+  }
+
+  return Create(std::move(root_cpuset_fd), "/", nullptr);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(base::unique_fd base_fd,
+                                              const std::string& name,
+                                              CpuSet* parent) {
+  DirectoryReader directory(base::unique_fd(dup(base_fd)));
+  if (!directory) {
+    ALOGE("CpuSet::Create: Failed to opendir %s cpuset: %s", name.c_str(),
+          strerror(directory.GetError()));
+    return nullptr;
+  }
+
+  std::unique_ptr<CpuSet> group(
+      new CpuSet(parent, name, base::unique_fd(dup(base_fd))));
+  path_map_.insert(std::make_pair(group->path(), group.get()));
+
+  while (dirent* entry = directory.Next()) {
+    if (entry->d_type == DT_DIR) {
+      std::string directory_name(entry->d_name);
+
+      if (directory_name == "." || directory_name == "..")
+        continue;
+
+      base::unique_fd entry_fd(
+          openat(base_fd.get(), directory_name.c_str(), kDirectoryFlags));
+      if (entry_fd.get() >= 0) {
+        auto child =
+            Create(std::move(entry_fd), directory_name.c_str(), group.get());
+
+        if (child)
+          group->AddChild(std::move(child));
+        else
+          return nullptr;
+      } else {
+        ALOGE("CpuSet::Create: Failed to openat \"%s\": %s", entry->d_name,
+              strerror(errno));
+        return nullptr;
+      }
+    }
+  }
+
+  return group;
+}
+
+CpuSet* CpuSetManager::Lookup(const std::string& path) {
+  auto search = path_map_.find(path);
+  if (search != path_map_.end())
+    return search->second;
+  else
+    return nullptr;
+}
+
+std::vector<CpuSet*> CpuSetManager::GetCpuSets() {
+  std::vector<CpuSet*> sets(path_map_.size());
+
+  for (const auto& pair : path_map_) {
+    sets.push_back(pair.second);
+  }
+
+  return sets;
+}
+
+std::string CpuSetManager::DumpState() const {
+  size_t max_path = 0;
+  std::vector<CpuSet*> sets;
+
+  for (const auto& pair : path_map_) {
+    max_path = std::max(max_path, pair.second->path().length());
+    sets.push_back(pair.second);
+  }
+
+  std::sort(sets.begin(), sets.end(), [](const CpuSet* a, const CpuSet* b) {
+    return a->path() < b->path();
+  });
+
+  std::ostringstream stream;
+
+  stream << std::left;
+  stream << std::setw(max_path) << "Path";
+  stream << " ";
+  stream << std::setw(6) << "CPUs";
+  stream << " ";
+  stream << std::setw(6) << "Tasks";
+  stream << std::endl;
+
+  stream << std::string(max_path, '_');
+  stream << " ";
+  stream << std::string(6, '_');
+  stream << " ";
+  stream << std::string(6, '_');
+  stream << std::endl;
+
+  for (const auto set : sets) {
+    stream << std::left;
+    stream << std::setw(max_path) << set->path();
+    stream << " ";
+    stream << std::right;
+    stream << std::setw(6) << set->GetCpuList();
+    stream << " ";
+    stream << std::setw(6) << set->GetTasks().size();
+    stream << std::endl;
+  }
+
+  return stream.str();
+}
+
+void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
+  auto root = Lookup("/");
+  if (!root) {
+    ALOGE("CpuSetManager::MoveUnboundTasks: Failed to find root cpuset!");
+    return;
+  }
+
+  auto target = Lookup(target_set);
+  if (!target) {
+    ALOGE(
+        "CpuSetManager::MoveUnboundTasks: Failed to find target cpuset \"%s\"!",
+        target_set.c_str());
+    return;
+  }
+
+  auto cpu_list = root->GetCpuList();
+
+  for (auto task_id : root->GetTasks()) {
+    Task task(task_id);
+
+    // Move only unbound kernel threads to the target cpuset.
+    if (task.cpus_allowed_list() == cpu_list &&
+        task.parent_process_id() == kKernelThreadDaemonPid) {
+      ALOGD_IF(TRACE,
+               "CpuSetManager::MoveUnboundTasks: Moving task_id=%d name=%s to "
+               "target_set=%s tgid=%d ppid=%d.",
+               task_id, task.name().c_str(), target_set.c_str(),
+               task.thread_group_id(), task.parent_process_id());
+
+      const int ret = target->AttachTask(task_id);
+      ALOGW_IF(ret < 0 && ret != -EINVAL,
+               "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
+               "to cpuset=%s: %s",
+               task_id, target_set.c_str(), strerror(-ret));
+    } else {
+      ALOGD_IF(TRACE,
+               "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
+               task_id, task.name().c_str(), task.cpus_allowed_list().c_str());
+    }
+  }
+}
+
+CpuSet::CpuSet(CpuSet* parent, const std::string& name,
+               base::unique_fd&& cpuset_fd)
+    : parent_(parent), name_(name), cpuset_fd_(std::move(cpuset_fd)) {
+  if (parent_ == nullptr)
+    path_ = name_;
+  else if (parent_->IsRoot())
+    path_ = parent_->name() + name_;
+  else
+    path_ = parent_->path() + "/" + name_;
+
+  ALOGI("CpuSet::CpuSet: path=%s", path().c_str());
+}
+
+base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
+  return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
+  return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+base::unique_fd CpuSet::OpenFile(const std::string& name, int flags) const {
+  const std::string relative_path = "./" + name;
+  return base::unique_fd(
+      openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+}
+
+UniqueFile CpuSet::OpenFilePointer(const std::string& name, int flags) const {
+  const std::string relative_path = "./" + name;
+  base::unique_fd fd(openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+  if (fd.get() < 0) {
+    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to open %s/%s: %s",
+          path_.c_str(), name.c_str(), strerror(errno));
+    return nullptr;
+  }
+
+  UniqueFile fp(fdopen(fd.release(), "r"));
+  if (!fp)
+    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to fdopen %s/%s: %s",
+          path_.c_str(), name.c_str(), strerror(errno));
+
+  return fp;
+}
+
+int CpuSet::AttachTask(pid_t task_id) const {
+  auto file = OpenFile("tasks", O_RDWR);
+  if (file.get() >= 0) {
+    std::ostringstream stream;
+    stream << task_id;
+    std::string value = stream.str();
+
+    const bool ret = base::WriteStringToFd(value, file.get());
+    return !ret ? -errno : 0;
+  } else {
+    ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
+          strerror(errno));
+    return -errno;
+  }
+}
+
+std::vector<pid_t> CpuSet::GetTasks() const {
+  std::vector<pid_t> tasks;
+
+  if (auto file = OpenFilePointer("tasks")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      pid_t task_id = std::strtol(line.c_str(), nullptr, 10);
+      tasks.push_back(task_id);
+    }
+  }
+
+  return tasks;
+}
+
+std::string CpuSet::GetCpuList() const {
+  if (auto file = OpenPropertyFilePointer("cpus")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    std::string line;
+    if (std::getline(file_stream, line))
+      return line;
+  }
+
+  ALOGE("CpuSet::GetCpuList: Failed to read cpu list!!!");
+  return "";
+}
+
+void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
+  children_.push_back(std::move(child));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
new file mode 100644
index 0000000..fcf280a
--- /dev/null
+++ b/services/vr/performanced/cpu_set.h
@@ -0,0 +1,105 @@
+#ifndef ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+#define ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+
+#include <fcntl.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+class CpuSet {
+ public:
+  // Returns the parent group for this group, if any. This pointer is owned by
+  // the group hierarchy and is only valid as long as the hierarchy is valid.
+  CpuSet* parent() const { return parent_; }
+  std::string name() const { return name_; }
+  std::string path() const { return path_; }
+
+  bool IsRoot() const { return parent_ == nullptr; }
+
+  std::string GetCpuList() const;
+
+  int AttachTask(pid_t task_id) const;
+  std::vector<pid_t> GetTasks() const;
+
+ private:
+  friend class CpuSetManager;
+
+  CpuSet(CpuSet* parent, const std::string& name, base::unique_fd&& cpuset_fd);
+
+  void AddChild(std::unique_ptr<CpuSet> child);
+
+  base::unique_fd OpenPropertyFile(const std::string& name) const;
+  UniqueFile OpenPropertyFilePointer(const std::string& name) const;
+
+  base::unique_fd OpenFile(const std::string& name, int flags = O_RDONLY) const;
+  UniqueFile OpenFilePointer(const std::string& name,
+                             int flags = O_RDONLY) const;
+
+  CpuSet* parent_;
+  std::string name_;
+  std::string path_;
+  base::unique_fd cpuset_fd_;
+  std::vector<std::unique_ptr<CpuSet>> children_;
+
+  static void SetPrefixEnabled(bool enabled) { prefix_enabled_ = enabled; }
+  static bool prefix_enabled_;
+
+  CpuSet(const CpuSet&) = delete;
+  void operator=(const CpuSet&) = delete;
+};
+
+class CpuSetManager {
+ public:
+  CpuSetManager() {}
+
+  // Creats a CpuSet hierarchy by walking the directory tree starting at
+  // |cpuset_root|. This argument must be the path to the root cpuset for the
+  // system, which is usually /dev/cpuset.
+  void Load(const std::string& cpuset_root);
+
+  // Lookup and return a CpuSet from a cpuset path. Ownership of the pointer
+  // DOES NOT pass to the caller; the pointer remains valid as long as the
+  // CpuSet hierarchy is valid.
+  CpuSet* Lookup(const std::string& path);
+
+  // Returns a vector of all the cpusets found at initializaiton. Ownership of
+  // the pointers to CpuSets DOES NOT pass to the caller; the pointers remain
+  // valid as long as the CpuSet hierarchy is valid.
+  std::vector<CpuSet*> GetCpuSets();
+
+  // Moves all unbound tasks from the root set into the target set. This is used
+  // to shield the system from interference from unbound kernel threads.
+  void MoveUnboundTasks(const std::string& target_set);
+
+  std::string DumpState() const;
+
+  operator bool() const { return root_set_ != nullptr; }
+
+ private:
+  // Creates a CpuSet from a path to a cpuset cgroup directory. Recursively
+  // creates child groups for each directory found under |path|.
+  std::unique_ptr<CpuSet> Create(const std::string& path);
+  std::unique_ptr<CpuSet> Create(base::unique_fd base_fd,
+                                 const std::string& name, CpuSet* parent);
+
+  std::unique_ptr<CpuSet> root_set_;
+  std::unordered_map<std::string, CpuSet*> path_map_;
+
+  CpuSetManager(const CpuSetManager&) = delete;
+  void operator=(const CpuSetManager&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_CPU_SET_H_
diff --git a/services/vr/performanced/directory_reader.h b/services/vr/performanced/directory_reader.h
new file mode 100644
index 0000000..7d7ecc5
--- /dev/null
+++ b/services/vr/performanced/directory_reader.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+#define ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dvr {
+
+// Utility class around readdir() that handles automatic cleanup.
+class DirectoryReader {
+ public:
+  explicit DirectoryReader(base::unique_fd directory_fd) {
+    directory_ = fdopendir(directory_fd.get());
+    error_ = errno;
+    if (directory_ != nullptr)
+      directory_fd.release();
+  }
+
+  ~DirectoryReader() {
+    if (directory_)
+      closedir(directory_);
+  }
+
+  bool IsValid() const { return directory_ != nullptr; }
+  explicit operator bool() const { return IsValid(); }
+  int GetError() const { return error_; }
+
+  // Returns a pointer to a dirent describing the next directory entry. The
+  // pointer is only valid unitl the next call to Next() or the DirectoryReader
+  // is destroyed. Returns nullptr when the end of the directory is reached.
+  dirent* Next() {
+    if (directory_)
+      return readdir(directory_);
+    else
+      return nullptr;
+  }
+
+ private:
+  DIR* directory_;
+  int error_;
+
+  DirectoryReader(const DirectoryReader&) = delete;
+  void operator=(const DirectoryReader&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
diff --git a/services/vr/performanced/main.cpp b/services/vr/performanced/main.cpp
new file mode 100644
index 0000000..ca66c71
--- /dev/null
+++ b/services/vr/performanced/main.cpp
@@ -0,0 +1,76 @@
+#include <errno.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+#include <sys/resource.h>
+#include <utils/threads.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+#include <private/android_filesystem_config.h>
+
+#include "performance_service.h"
+
+namespace {
+
+// Annoying that sys/capability.h doesn't define this directly.
+constexpr int kMaxCapNumber = (CAP_TO_INDEX(CAP_LAST_CAP) + 1);
+
+}  // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+  int ret = -1;
+
+  struct __user_cap_header_struct capheader;
+  struct __user_cap_data_struct capdata[kMaxCapNumber];
+
+  std::shared_ptr<android::pdx::Service> service;
+  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+  ALOGI("Starting up...");
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  // Keep capabilities when switching UID to AID_SYSTEM.
+  ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+  CHECK_ERROR(ret < 0, error, "Failed to set KEEPCAPS: %s", strerror(errno));
+
+  // Set UID and GID to system.
+  ret = setresgid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+  CHECK_ERROR(ret < 0, error, "Failed to set GID: %s", strerror(errno));
+  ret = setresuid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+  CHECK_ERROR(ret < 0, error, "Failed to set UID: %s", strerror(errno));
+
+  // Keep CAP_SYS_NICE, allowing control of scheduler class, priority, and
+  // cpuset for other tasks in the system.
+  memset(&capheader, 0, sizeof(capheader));
+  memset(&capdata, 0, sizeof(capdata));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capdata[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+  capdata[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+
+  // Drop all caps but the ones configured above.
+  ret = capset(&capheader, capdata);
+  CHECK_ERROR(ret < 0, error, "Could not set capabilities: %s",
+              strerror(errno));
+
+  dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher.");
+
+  service = android::dvr::PerformanceService::Create();
+  CHECK_ERROR(!service, error, "Failed to create performance service service.");
+  dispatcher->AddService(service);
+
+  ALOGI("Entering message loop.");
+
+  ret = dispatcher->EnterDispatchLoop();
+  CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+              strerror(-ret));
+
+error:
+  return ret;
+}
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
new file mode 100644
index 0000000..955c661
--- /dev/null
+++ b/services/vr/performanced/performance_service.cpp
@@ -0,0 +1,196 @@
+#include "performance_service.h"
+
+#include <sched.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/performance_rpc.h>
+
+#include "task.h"
+
+// This prctl is only available in Android kernels.
+#define PR_SET_TIMERSLACK_PID 41
+
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::default_transport::Endpoint;
+
+namespace {
+
+const char kCpuSetBasePath[] = "/dev/cpuset";
+
+constexpr unsigned long kTimerSlackForegroundNs = 50000;
+constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+PerformanceService::PerformanceService()
+    : BASE("PerformanceService",
+           Endpoint::Create(PerformanceRPC::kClientPath)) {
+  cpuset_.Load(kCpuSetBasePath);
+
+  Task task(getpid());
+  ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
+        task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
+
+  // Errors here are checked in IsInitialized().
+  sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
+  sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
+
+  const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
+  const int fifo_low = sched_fifo_min_priority_;
+  const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
+
+  // TODO(eieio): Make this configurable on the command line.
+  cpuset_.MoveUnboundTasks("/kernel");
+
+  // Setup the scheduler classes.
+  scheduler_classes_ = {
+      {"audio:low",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium}},
+      {"audio:high",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium + 3}},
+      {"graphics",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium}},
+      {"graphics:low",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium}},
+      {"graphics:high",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium + 2}},
+      {"sensors",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_low}},
+      {"sensors:low",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_low}},
+      {"sensors:high",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_low + 1}},
+      {"normal",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_NORMAL,
+        .priority = 0}},
+      {"foreground",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_NORMAL,
+        .priority = 0}},
+      {"background",
+       {.timer_slack = kTimerSlackBackgroundNs,
+        .scheduler_policy = SCHED_BATCH,
+        .priority = 0}},
+      {"batch",
+       {.timer_slack = kTimerSlackBackgroundNs,
+        .scheduler_policy = SCHED_BATCH,
+        .priority = 0}},
+  };
+}
+
+bool PerformanceService::IsInitialized() const {
+  return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
+         sched_fifo_max_priority_ >= 0;
+}
+
+std::string PerformanceService::DumpState(size_t /*max_length*/) {
+  return cpuset_.DumpState();
+}
+
+int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
+                                          const std::string& partition) {
+  Task task(task_id);
+  if (!task || task.thread_group_id() != message.GetProcessId())
+    return -EINVAL;
+
+  auto target_set = cpuset_.Lookup(partition);
+  if (!target_set)
+    return -ENOENT;
+
+  const auto attach_error = target_set->AttachTask(task_id);
+  if (attach_error)
+    return attach_error;
+
+  return 0;
+}
+
+int PerformanceService::OnSetSchedulerClass(
+    Message& message, pid_t task_id, const std::string& scheduler_class) {
+  // Make sure the task id is valid and belongs to the sending process.
+  Task task(task_id);
+  if (!task || task.thread_group_id() != message.GetProcessId())
+    return -EINVAL;
+
+  struct sched_param param;
+
+  // TODO(eieio): Apply rules based on the requesting process. Applications are
+  // only allowed one audio thread that runs at SCHED_FIFO. System services can
+  // have more than one.
+  auto search = scheduler_classes_.find(scheduler_class);
+  if (search != scheduler_classes_.end()) {
+    auto config = search->second;
+    param.sched_priority = config.priority;
+    sched_setscheduler(task_id, config.scheduler_policy, &param);
+    prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
+    ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
+          task_id, scheduler_class.c_str());
+    return 0;
+  } else {
+    ALOGE(
+        "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
+        "by task=%d.",
+        scheduler_class.c_str(), task_id);
+    return -EINVAL;
+  }
+}
+
+std::string PerformanceService::OnGetCpuPartition(Message& message,
+                                                  pid_t task_id) {
+  // Make sure the task id is valid and belongs to the sending process.
+  Task task(task_id);
+  if (!task || task.thread_group_id() != message.GetProcessId())
+    REPLY_ERROR_RETURN(message, EINVAL, "");
+
+  return task.GetCpuSetPath();
+}
+
+pdx::Status<void> PerformanceService::HandleMessage(Message& message) {
+  switch (message.GetOp()) {
+    case PerformanceRPC::SetCpuPartition::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+          *this, &PerformanceService::OnSetCpuPartition, message);
+      return {};
+
+    case PerformanceRPC::SetSchedulerClass::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+          *this, &PerformanceService::OnSetSchedulerClass, message);
+      return {};
+
+    case PerformanceRPC::GetCpuPartition::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
+          *this, &PerformanceService::OnGetCpuPartition, message);
+      return {};
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
new file mode 100644
index 0000000..34abba7
--- /dev/null
+++ b/services/vr/performanced/performance_service.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
+#define ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
+
+#include <string>
+#include <unordered_map>
+
+#include <pdx/service.h>
+
+#include "cpu_set.h"
+
+namespace android {
+namespace dvr {
+
+// PerformanceService manages compute partitions usings cpusets. Different
+// cpusets are assigned specific purposes and performance characteristics;
+// clients may request for threads to be moved into these cpusets to help
+// achieve system performance goals.
+class PerformanceService : public pdx::ServiceBase<PerformanceService> {
+ public:
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+  bool IsInitialized() const override;
+
+  std::string DumpState(size_t max_length) override;
+
+ private:
+  friend BASE;
+
+  PerformanceService();
+
+  int OnSetCpuPartition(pdx::Message& message, pid_t task_id,
+                        const std::string& partition);
+  int OnSetSchedulerClass(pdx::Message& message, pid_t task_id,
+                          const std::string& scheduler_class);
+  std::string OnGetCpuPartition(pdx::Message& message, pid_t task_id);
+
+  CpuSetManager cpuset_;
+
+  int sched_fifo_min_priority_;
+  int sched_fifo_max_priority_;
+
+  // Scheduler class config type.
+  struct SchedulerClassConfig {
+    unsigned long timer_slack;
+    int scheduler_policy;
+    int priority;
+  };
+
+  std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_;
+
+  PerformanceService(const PerformanceService&) = delete;
+  void operator=(const PerformanceService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp
new file mode 100644
index 0000000..b526082
--- /dev/null
+++ b/services/vr/performanced/performance_service_tests.cpp
@@ -0,0 +1,137 @@
+#include <errno.h>
+#include <sched.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <dvr/performance_client_api.h>
+#include <gtest/gtest.h>
+
+TEST(DISABLED_PerformanceTest, SetCpuPartition) {
+  int error;
+
+  // Test setting the the partition for the current task.
+  error = dvrSetCpuPartition(0, "/application/background");
+  EXPECT_EQ(0, error);
+
+  error = dvrSetCpuPartition(0, "/application/performance");
+  EXPECT_EQ(0, error);
+
+  // Test setting the partition for one of our tasks.
+  bool done = false;
+  pid_t task_id = 0;
+  std::mutex mutex;
+  std::condition_variable done_condition, id_condition;
+
+  std::thread thread([&] {
+    std::unique_lock<std::mutex> lock(mutex);
+
+    task_id = gettid();
+    id_condition.notify_one();
+
+    done_condition.wait(lock, [&done] { return done; });
+  });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    id_condition.wait(lock, [&task_id] { return task_id != 0; });
+  }
+  EXPECT_NE(0, task_id);
+
+  error = dvrSetCpuPartition(task_id, "/application");
+  EXPECT_EQ(0, error);
+
+  {
+    std::lock_guard<std::mutex> lock(mutex);
+    done = true;
+    done_condition.notify_one();
+  }
+  thread.join();
+
+  // Test setting the partition for a task that isn't valid using
+  // the task id of the thread that we just joined. Technically the
+  // id could wrap around by the time we get here, but this is
+  // extremely unlikely.
+  error = dvrSetCpuPartition(task_id, "/application");
+  EXPECT_EQ(-EINVAL, error);
+
+  // Test setting the partition for a task that doesn't belong to us.
+  error = dvrSetCpuPartition(1, "/application");
+  EXPECT_EQ(-EINVAL, error);
+
+  // Test setting the partition to one that doesn't exist.
+  error = dvrSetCpuPartition(0, "/foobar");
+  EXPECT_EQ(-ENOENT, error);
+}
+
+TEST(PerformanceTest, SetSchedulerClass) {
+  int error;
+
+  // TODO(eieio): Test all supported scheduler classes and priority levels.
+
+  error = dvrSetSchedulerClass(0, "background");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_BATCH, sched_getscheduler(0));
+
+  error = dvrSetSchedulerClass(0, "audio:low");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+  error = dvrSetSchedulerClass(0, "normal");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+
+  error = dvrSetSchedulerClass(0, "foobar");
+  EXPECT_EQ(-EINVAL, error);
+}
+
+TEST(PerformanceTest, SchedulerClassResetOnFork) {
+  int error;
+
+  error = dvrSetSchedulerClass(0, "graphics:high");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+  int scheduler = -1;
+  std::thread thread([&]() { scheduler = sched_getscheduler(0); });
+  thread.join();
+
+  EXPECT_EQ(SCHED_NORMAL, scheduler);
+
+  // Return to SCHED_NORMAL.
+  error = dvrSetSchedulerClass(0, "normal");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+}
+
+TEST(PerformanceTest, GetCpuPartition) {
+  int error;
+  char partition[PATH_MAX + 1];
+
+  error = dvrSetCpuPartition(0, "/");
+  ASSERT_EQ(0, error);
+
+  error = dvrGetCpuPartition(0, partition, sizeof(partition));
+  EXPECT_EQ(0, error);
+  EXPECT_EQ("/", std::string(partition));
+
+  error = dvrSetCpuPartition(0, "/application");
+  EXPECT_EQ(0, error);
+
+  error = dvrGetCpuPartition(0, partition, sizeof(partition));
+  EXPECT_EQ(0, error);
+  EXPECT_EQ("/application", std::string(partition));
+
+  // Test passing a buffer that is too short.
+  error = dvrGetCpuPartition(0, partition, 5);
+  EXPECT_EQ(-ENOBUFS, error);
+
+  // Test getting the partition for a task that doesn't belong to us.
+  error = dvrGetCpuPartition(1, partition, sizeof(partition));
+  EXPECT_EQ(-EINVAL, error);
+
+  // Test passing a nullptr value for partition buffer.
+  error = dvrGetCpuPartition(0, nullptr, sizeof(partition));
+  EXPECT_EQ(-EINVAL, error);
+}
diff --git a/services/vr/performanced/performanced.rc b/services/vr/performanced/performanced.rc
new file mode 100644
index 0000000..2605a47
--- /dev/null
+++ b/services/vr/performanced/performanced.rc
@@ -0,0 +1,6 @@
+service performanced /system/bin/performanced
+  class core
+  user root
+  group system readproc
+  writepid /dev/cpuset/tasks
+  socket pdx/system/performance/client stream 0666 system system u:object_r:pdx_performance_client_endpoint_socket:s0
diff --git a/services/vr/performanced/stdio_filebuf.h b/services/vr/performanced/stdio_filebuf.h
new file mode 100644
index 0000000..5988aa8
--- /dev/null
+++ b/services/vr/performanced/stdio_filebuf.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
+// Copyright (c) 2016 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
+#define ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
+
+#include <cstdio>
+#include <istream>
+#include <locale>
+#include <streambuf>
+
+namespace android {
+namespace dvr {
+
+// An implementation of std::basic_streambuf backed by a FILE pointer. This is
+// ported from the internal llvm-libc++ support for std::cin. It's really
+// unfortunate that we have to do this, but the C++11 standard is too pendantic
+// to support creating streams from file descriptors or FILE pointers. This
+// implementation uses all standard interfaces, except for the call to
+// std::__throw_runtime_error(), which is only needed to deal with exceeding
+// locale encoding limits. This class is meant to be used for reading system
+// files, which don't require exotic locale support, so this call could be
+// removed in the future, if necessary.
+//
+// Original source file: llvm-libcxx/llvm-libc++/include/__std_stream
+// Original class name: __stdinbuf
+//
+template <class _CharT>
+class stdio_filebuf
+    : public std::basic_streambuf<_CharT, std::char_traits<_CharT> > {
+ public:
+  typedef _CharT char_type;
+  typedef std::char_traits<char_type> traits_type;
+  typedef typename traits_type::int_type int_type;
+  typedef typename traits_type::pos_type pos_type;
+  typedef typename traits_type::off_type off_type;
+  typedef typename traits_type::state_type state_type;
+
+  explicit stdio_filebuf(FILE* __fp);
+  ~stdio_filebuf() override;
+
+ protected:
+  virtual int_type underflow() override;
+  virtual int_type uflow() override;
+  virtual int_type pbackfail(int_type __c = traits_type::eof()) override;
+  virtual void imbue(const std::locale& __loc) override;
+
+ private:
+  FILE* __file_;
+  const std::codecvt<char_type, char, state_type>* __cv_;
+  state_type __st_;
+  int __encoding_;
+  int_type __last_consumed_;
+  bool __last_consumed_is_next_;
+  bool __always_noconv_;
+
+  stdio_filebuf(const stdio_filebuf&);
+  stdio_filebuf& operator=(const stdio_filebuf&);
+
+  int_type __getchar(bool __consume);
+
+  static const int __limit = 8;
+};
+
+template <class _CharT>
+stdio_filebuf<_CharT>::stdio_filebuf(FILE* __fp)
+    : __file_(__fp),
+      __last_consumed_(traits_type::eof()),
+      __last_consumed_is_next_(false) {
+  imbue(this->getloc());
+}
+
+template <class _CharT>
+stdio_filebuf<_CharT>::~stdio_filebuf() {
+  if (__file_)
+    fclose(__file_);
+}
+
+template <class _CharT>
+void stdio_filebuf<_CharT>::imbue(const std::locale& __loc) {
+  __cv_ = &std::use_facet<std::codecvt<char_type, char, state_type> >(__loc);
+  __encoding_ = __cv_->encoding();
+  __always_noconv_ = __cv_->always_noconv();
+  if (__encoding_ > __limit)
+    std::__throw_runtime_error("unsupported locale for standard io");
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::underflow() {
+  return __getchar(false);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::uflow() {
+  return __getchar(true);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::__getchar(
+    bool __consume) {
+  if (__last_consumed_is_next_) {
+    int_type __result = __last_consumed_;
+    if (__consume) {
+      __last_consumed_ = traits_type::eof();
+      __last_consumed_is_next_ = false;
+    }
+    return __result;
+  }
+  char __extbuf[__limit];
+  int __nread = std::max(1, __encoding_);
+  for (int __i = 0; __i < __nread; ++__i) {
+    int __c = getc(__file_);
+    if (__c == EOF)
+      return traits_type::eof();
+    __extbuf[__i] = static_cast<char>(__c);
+  }
+  char_type __1buf;
+  if (__always_noconv_)
+    __1buf = static_cast<char_type>(__extbuf[0]);
+  else {
+    const char* __enxt;
+    char_type* __inxt;
+    std::codecvt_base::result __r;
+    do {
+      state_type __sv_st = __st_;
+      __r = __cv_->in(__st_, __extbuf, __extbuf + __nread, __enxt, &__1buf,
+                      &__1buf + 1, __inxt);
+      switch (__r) {
+        case std::codecvt_base::ok:
+          break;
+        case std::codecvt_base::partial:
+          __st_ = __sv_st;
+          if (__nread == sizeof(__extbuf))
+            return traits_type::eof();
+          {
+            int __c = getc(__file_);
+            if (__c == EOF)
+              return traits_type::eof();
+            __extbuf[__nread] = static_cast<char>(__c);
+          }
+          ++__nread;
+          break;
+        case std::codecvt_base::error:
+          return traits_type::eof();
+        case std::codecvt_base::noconv:
+          __1buf = static_cast<char_type>(__extbuf[0]);
+          break;
+      }
+    } while (__r == std::codecvt_base::partial);
+  }
+  if (!__consume) {
+    for (int __i = __nread; __i > 0;) {
+      if (ungetc(traits_type::to_int_type(__extbuf[--__i]), __file_) == EOF)
+        return traits_type::eof();
+    }
+  } else
+    __last_consumed_ = traits_type::to_int_type(__1buf);
+  return traits_type::to_int_type(__1buf);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::pbackfail(
+    int_type __c) {
+  if (traits_type::eq_int_type(__c, traits_type::eof())) {
+    if (!__last_consumed_is_next_) {
+      __c = __last_consumed_;
+      __last_consumed_is_next_ =
+          !traits_type::eq_int_type(__last_consumed_, traits_type::eof());
+    }
+    return __c;
+  }
+  if (__last_consumed_is_next_) {
+    char __extbuf[__limit];
+    char* __enxt;
+    const char_type __ci = traits_type::to_char_type(__last_consumed_);
+    const char_type* __inxt;
+    switch (__cv_->out(__st_, &__ci, &__ci + 1, __inxt, __extbuf,
+                       __extbuf + sizeof(__extbuf), __enxt)) {
+      case std::codecvt_base::ok:
+        break;
+      case std::codecvt_base::noconv:
+        __extbuf[0] = static_cast<char>(__last_consumed_);
+        __enxt = __extbuf + 1;
+        break;
+      case std::codecvt_base::partial:
+      case std::codecvt_base::error:
+        return traits_type::eof();
+    }
+    while (__enxt > __extbuf)
+      if (ungetc(*--__enxt, __file_) == EOF)
+        return traits_type::eof();
+  }
+  __last_consumed_ = __c;
+  __last_consumed_is_next_ = true;
+  return __c;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
diff --git a/services/vr/performanced/string_trim.h b/services/vr/performanced/string_trim.h
new file mode 100644
index 0000000..7094e9f
--- /dev/null
+++ b/services/vr/performanced/string_trim.h
@@ -0,0 +1,46 @@
+#ifndef ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
+#define ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
+
+#include <functional>
+#include <locale>
+#include <string>
+
+namespace android {
+namespace dvr {
+
+// Trims whitespace from the left side of |subject| and returns the result as a
+// new string.
+inline std::string LeftTrim(std::string subject) {
+  subject.erase(subject.begin(),
+                std::find_if(subject.begin(), subject.end(),
+                             std::not1(std::ptr_fun<int, int>(std::isspace))));
+  return subject;
+}
+
+// Trims whitespace from the right side of |subject| and returns the result as a
+// new string.
+inline std::string RightTrim(std::string subject) {
+  subject.erase(std::find_if(subject.rbegin(), subject.rend(),
+                             std::not1(std::ptr_fun<int, int>(std::isspace)))
+                    .base(),
+                subject.end());
+  return subject;
+}
+
+// Trims whitespace from the both sides of |subject| and returns the result as a
+// new string.
+inline std::string Trim(std::string subject) {
+  subject.erase(subject.begin(),
+                std::find_if(subject.begin(), subject.end(),
+                             std::not1(std::ptr_fun<int, int>(std::isspace))));
+  subject.erase(std::find_if(subject.rbegin(), subject.rend(),
+                             std::not1(std::ptr_fun<int, int>(std::isspace)))
+                    .base(),
+                subject.end());
+  return subject;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
diff --git a/services/vr/performanced/task.cpp b/services/vr/performanced/task.cpp
new file mode 100644
index 0000000..1175a7b
--- /dev/null
+++ b/services/vr/performanced/task.cpp
@@ -0,0 +1,163 @@
+#include "task.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <stdio.h>
+
+#include <cctype>
+#include <cstdlib>
+#include <memory>
+#include <sstream>
+
+#include <android-base/unique_fd.h>
+
+#include "stdio_filebuf.h"
+#include "string_trim.h"
+
+namespace {
+
+const char kProcBase[] = "/proc";
+
+android::base::unique_fd OpenTaskDirectory(pid_t task_id) {
+  std::ostringstream stream;
+  stream << kProcBase << "/" << task_id;
+
+  return android::base::unique_fd(
+      open(stream.str().c_str(), O_RDONLY | O_DIRECTORY));
+}
+
+void ParseUidStatusField(const std::string& value, std::array<int, 4>& ids) {
+  const char* start = value.c_str();
+
+  ids[0] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[1] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[2] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[3] = std::strtol(start, const_cast<char**>(&start), 10);
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+Task::Task(pid_t task_id)
+    : task_id_(task_id),
+      thread_group_id_(-1),
+      parent_process_id_(-1),
+      thread_count_(0),
+      cpus_allowed_mask_(0) {
+  task_fd_ = OpenTaskDirectory(task_id_);
+  ALOGE_IF(task_fd_.get() < 0,
+           "Task::Task: Failed to open task directory for task_id=%d: %s",
+           task_id, strerror(errno));
+
+  ReadStatusFields();
+
+  ALOGD_IF(TRACE, "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x",
+           task_id_, name_.c_str(), thread_group_id_, parent_process_id_,
+           cpus_allowed_mask_);
+}
+
+base::unique_fd Task::OpenTaskFile(const std::string& name) const {
+  const std::string relative_path = "./" + name;
+  return base::unique_fd(
+      openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+}
+
+UniqueFile Task::OpenTaskFilePointer(const std::string& name) const {
+  const std::string relative_path = "./" + name;
+  base::unique_fd fd(openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+  if (fd.get() < 0) {
+    ALOGE("Task::OpenTaskFilePointer: Failed to open /proc/%d/%s: %s", task_id_,
+          name.c_str(), strerror(errno));
+    return nullptr;
+  }
+
+  UniqueFile fp(fdopen(fd.release(), "r"));
+  if (!fp)
+    ALOGE("Task::OpenTaskFilePointer: Failed to fdopen /proc/%d/%s: %s",
+          task_id_, name.c_str(), strerror(errno));
+
+  return fp;
+}
+
+std::string Task::GetStatusField(const std::string& field) const {
+  if (auto file = OpenTaskFilePointer("status")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      auto offset = line.find(field);
+
+      ALOGD_IF(TRACE,
+               "Task::GetStatusField: field=\"%s\" line=\"%s\" offset=%zd",
+               field.c_str(), line.c_str(), offset);
+
+      if (offset == std::string::npos)
+        continue;
+
+      // The status file has lines with the format <field>:<value>. Extract the
+      // value after the colon.
+      return Trim(line.substr(offset + field.size() + 1));
+    }
+  }
+
+  return "[unknown]";
+}
+
+void Task::ReadStatusFields() {
+  if (auto file = OpenTaskFilePointer("status")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      auto offset = line.find(":");
+      if (offset == std::string::npos) {
+        ALOGW("ReadStatusFields: Failed to find delimiter \":\" in line=\"%s\"",
+              line.c_str());
+        continue;
+      }
+
+      std::string key = line.substr(0, offset);
+      std::string value = Trim(line.substr(offset + 1));
+
+      ALOGD_IF(TRACE, "Task::ReadStatusFields: key=\"%s\" value=\"%s\"",
+               key.c_str(), value.c_str());
+
+      if (key == "Name")
+        name_ = value;
+      else if (key == "Tgid")
+        thread_group_id_ = std::strtol(value.c_str(), nullptr, 10);
+      else if (key == "PPid")
+        parent_process_id_ = std::strtol(value.c_str(), nullptr, 10);
+      else if (key == "Uid")
+        ParseUidStatusField(value, user_id_);
+      else if (key == "Gid")
+        ParseUidStatusField(value, group_id_);
+      else if (key == "Threads")
+        thread_count_ = std::strtoul(value.c_str(), nullptr, 10);
+      else if (key == "Cpus_allowed")
+        cpus_allowed_mask_ = std::strtoul(value.c_str(), nullptr, 16);
+      else if (key == "Cpus_allowed_list")
+        cpus_allowed_list_ = value;
+    }
+  }
+}
+
+std::string Task::GetCpuSetPath() const {
+  if (auto file = OpenTaskFilePointer("cpuset")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    std::string line = "";
+    std::getline(file_stream, line);
+
+    return Trim(line);
+  } else {
+    return "";
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/performanced/task.h b/services/vr/performanced/task.h
new file mode 100644
index 0000000..4a3b7f2
--- /dev/null
+++ b/services/vr/performanced/task.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_PERFORMANCED_TASK_H_
+#define ANDROID_DVR_PERFORMANCED_TASK_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+// Task provides access to task-related information from the procfs
+// pseudo-filesystem.
+class Task {
+ public:
+  explicit Task(pid_t task_id);
+
+  bool IsValid() const { return task_fd_.get() >= 0; }
+  explicit operator bool() const { return IsValid(); }
+
+  pid_t task_id() const { return task_id_; }
+  std::string name() const { return name_; }
+  pid_t thread_group_id() const { return thread_group_id_; }
+  pid_t parent_process_id() const { return parent_process_id_; }
+  size_t thread_count() const { return thread_count_; }
+  uint32_t cpus_allowed_mask() const { return cpus_allowed_mask_; }
+  const std::string& cpus_allowed_list() const { return cpus_allowed_list_; }
+  const std::array<int, 4>& user_id() const { return user_id_; }
+  const std::array<int, 4>& group_id() const { return group_id_; }
+
+  // Indices into user and group id arrays.
+  enum {
+    kUidReal = 0,
+    kUidEffective,
+    kUidSavedSet,
+    kUidFilesystem,
+  };
+
+  std::string GetCpuSetPath() const;
+
+ private:
+  pid_t task_id_;
+  base::unique_fd task_fd_;
+
+  // Fields read from /proc/<task_id_>/status.
+  std::string name_;
+  pid_t thread_group_id_;
+  pid_t parent_process_id_;
+  std::array<int, 4> user_id_;
+  std::array<int, 4> group_id_;
+  size_t thread_count_;
+  uint32_t cpus_allowed_mask_;
+  std::string cpus_allowed_list_;
+
+  // Opens the file /proc/<task_id_>/|name| and returns the open file
+  // descriptor.
+  base::unique_fd OpenTaskFile(const std::string& name) const;
+
+  // Similar to OpenTaskFile() but returns a file pointer.
+  UniqueFile OpenTaskFilePointer(const std::string& name) const;
+
+  // Reads the field named |field| from /proc/<task_id_>/status.
+  std::string GetStatusField(const std::string& field) const;
+
+  // Reads a subset of the fields in /proc/<task_id_>/status.
+  void ReadStatusFields();
+
+  Task(const Task&) = delete;
+  void operator=(const Task&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_TASK_H_
diff --git a/services/vr/performanced/unique_file.h b/services/vr/performanced/unique_file.h
new file mode 100644
index 0000000..86e487a
--- /dev/null
+++ b/services/vr/performanced/unique_file.h
@@ -0,0 +1,20 @@
+#ifndef ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+#define ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+
+#include <stdio.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Utility to manage the lifetime of a file pointer.
+struct FileDeleter {
+  void operator()(FILE* fp) { fclose(fp); }
+};
+using UniqueFile = std::unique_ptr<FILE, FileDeleter>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
new file mode 100644
index 0000000..3d5dfb2
--- /dev/null
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -0,0 +1,116 @@
+
+
+// Touchpad implementation.
+
+src = [
+    "EvdevInjector.cpp",
+    "VirtualTouchpadEvdev.cpp",
+]
+
+shared_libs = [
+    "libbase",
+    "liblog",
+    "libutils",
+]
+
+cc_library {
+    srcs: src,
+    export_include_dirs: ["include"],
+    shared_libs: shared_libs,
+    cppflags: ["-std=c++11"],
+    cflags: ["-DLOG_TAG=\"VrVirtualTouchpad\""],
+    name: "libvirtualtouchpad",
+    tags: ["optional"],
+}
+
+// Touchpad unit tests.
+
+test_static_libs = [
+    "libcutils",
+    "libvirtualtouchpad",
+]
+
+test_shared_libs = [
+    "libbase",
+    "liblog",
+    "libutils",
+]
+
+test_src_files = ["tests/VirtualTouchpad_test.cpp"]
+
+cc_test {
+    srcs: test_src_files,
+    static_libs: test_static_libs,
+    shared_libs: test_shared_libs,
+    cppflags = [
+        "-std=c++11",
+    ],
+    host_ldlibs = [
+        "-llog",
+    ],
+    name: "VirtualTouchpad_test",
+    stl: "libc++_static",
+    tags: [ "optional" ],
+}
+
+// Service.
+
+service_src = [
+    "main.cpp",
+    "VirtualTouchpadService.cpp",
+    "aidl/android/dvr/VirtualTouchpadService.aidl",
+]
+
+service_static_libs = [
+    "libcutils",
+    "libvirtualtouchpad",
+]
+
+service_shared_libs = [
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libutils",
+]
+
+cc_binary {
+    srcs: service_src,
+    static_libs: service_static_libs,
+    shared_libs: service_shared_libs,
+    cppflags: ["-std=c++11"],
+    cflags: [
+        "-DLOG_TAG=\"VrVirtualTouchpad\"",
+    ],
+    host_ldlibs: ["-llog"],
+    name: "virtual_touchpad",
+    tags: ["optional"],
+    init_rc: ["virtual_touchpad.rc"],
+    compile_multilib: "64",
+    stl: "libc++_static",
+}
+
+// Touchpad client library.
+
+client_src = [
+    "VirtualTouchpadClient.cpp",
+    "DvrVirtualTouchpadClient.cpp",
+    "aidl/android/dvr/VirtualTouchpadService.aidl",
+]
+
+client_shared_libs = [
+    "libbase",
+    "libbinder",
+    "liblog",
+    "libutils",
+]
+
+cc_library {
+    srcs: client_src,
+    shared_libs: client_shared_libs,
+    cppflags: ["-std=c++11"],
+    cflags: ["-DLOG_TAG=\"VirtualTouchpadClient\""],
+    host_ldlibs: ["-llog"],
+    name: "libvirtualtouchpadclient",
+    tags: ["optional"],
+    export_include_dirs: ["include"],
+}
diff --git a/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp
new file mode 100644
index 0000000..eb152ed
--- /dev/null
+++ b/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp
@@ -0,0 +1,45 @@
+#include "VirtualTouchpadClient.h"
+#include "dvr/virtual_touchpad_client.h"
+
+struct DvrVirtualTouchpad {};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace {
+android::dvr::VirtualTouchpad* FromC(DvrVirtualTouchpad* client) {
+  return reinterpret_cast<android::dvr::VirtualTouchpad*>(client);
+}
+}  // namespace
+
+DvrVirtualTouchpad* dvrVirtualTouchpadCreate() {
+  return reinterpret_cast<DvrVirtualTouchpad*>(
+      android::dvr::VirtualTouchpadClient::Create().release());
+}
+
+void dvrVirtualTouchpadDestroy(DvrVirtualTouchpad* client) {
+  delete FromC(client);
+}
+
+int dvrVirtualTouchpadAttach(DvrVirtualTouchpad* client) {
+  return FromC(client)->Attach();
+}
+
+int dvrVirtualTouchpadDetach(DvrVirtualTouchpad* client) {
+  return FromC(client)->Detach();
+}
+
+int dvrVirtualTouchpadTouch(DvrVirtualTouchpad* client, int touchpad, float x,
+                            float y, float pressure) {
+  return FromC(client)->Touch(touchpad, x, y, pressure);
+}
+
+int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
+                                  int buttons) {
+  return FromC(client)->ButtonState(touchpad, buttons);
+}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
new file mode 100644
index 0000000..a4ccdd0
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -0,0 +1,316 @@
+#include "EvdevInjector.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <log/log.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+namespace android {
+namespace dvr {
+
+int EvdevInjector::UInput::Open() {
+  errno = 0;
+  fd_.reset(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+  if (fd_.get() < 0) {
+    ALOGE("couldn't open uinput (r=%d errno=%d)", fd_.get(), errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::Close() {
+  errno = 0;
+  fd_.reset();
+  return errno;
+}
+
+int EvdevInjector::UInput::Write(const void* buf, size_t count) {
+  ALOGV("UInput::Write(%zu, %02X...)", count, *static_cast<const char*>(buf));
+  errno = 0;
+  ssize_t r = write(fd_.get(), buf, count);
+  if (r != static_cast<ssize_t>(count)) {
+    ALOGE("write(%zu) failed (r=%zd errno=%d)", count, r, errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::IoctlSetInt(int request, int value) {
+  ALOGV("UInput::IoctlSetInt(0x%X, 0x%X)", request, value);
+  errno = 0;
+  if (const int status = ioctl(fd_.get(), request, value)) {
+    ALOGE("ioctl(%d, 0x%X, 0x%X) failed (r=%d errno=%d)", fd_.get(), request,
+          value, status, errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::IoctlVoid(int request) {
+  ALOGV("UInput::IoctlVoid(0x%X)", request);
+  errno = 0;
+  if (const int status = ioctl(fd_.get(), request)) {
+    ALOGE("ioctl(%d, 0x%X) failed (r=%d errno=%d)", fd_.get(), request, status,
+          errno);
+  }
+  return errno;
+}
+
+void EvdevInjector::Close() {
+  uinput_->Close();
+  state_ = State::CLOSED;
+}
+
+int EvdevInjector::ConfigureBegin(const char* device_name, int16_t bustype,
+                                  int16_t vendor, int16_t product,
+                                  int16_t version) {
+  ALOGV("ConfigureBegin %s 0x%04" PRIX16 " 0x%04" PRIX16 " 0x%04" PRIX16
+        " 0x%04" PRIX16 "",
+        device_name, bustype, vendor, product, version);
+  if (!device_name || strlen(device_name) >= UINPUT_MAX_NAME_SIZE) {
+    return Error(ERROR_DEVICE_NAME);
+  }
+  if (const int status = RequireState(State::NEW)) {
+    return status;
+  }
+  if (!uinput_) {
+    owned_uinput_.reset(new EvdevInjector::UInput());
+    uinput_ = owned_uinput_.get();
+  }
+  if (const int status = uinput_->Open()) {
+    // Without uinput we're dead in the water.
+    state_ = State::CLOSED;
+    return Error(status);
+  }
+  state_ = State::CONFIGURING;
+  // Initialize device setting structure.
+  memset(&uidev_, 0, sizeof(uidev_));
+  strncpy(uidev_.name, device_name, UINPUT_MAX_NAME_SIZE);
+  uidev_.id.bustype = bustype;
+  uidev_.id.vendor = vendor;
+  uidev_.id.product = product;
+  uidev_.id.version = version;
+  return 0;
+}
+
+int EvdevInjector::ConfigureInputProperty(int property) {
+  ALOGV("ConfigureInputProperty %d", property);
+  if (property < 0 || property >= INPUT_PROP_CNT) {
+    ALOGE("property 0x%X out of range [0,0x%X)", property, INPUT_PROP_CNT);
+    return Error(ERROR_PROPERTY_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_PROPBIT, property)) {
+    ALOGE("failed to set property %d", property);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureKey(uint16_t key) {
+  ALOGV("ConfigureKey 0x%02" PRIX16 "", key);
+  if (key < 0 || key >= KEY_CNT) {
+    ALOGE("key 0x%X out of range [0,0x%X)", key, KEY_CNT);
+    return Error(ERROR_KEY_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = EnableEventType(EV_KEY)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_KEYBIT, key)) {
+    ALOGE("failed to enable EV_KEY 0x%02" PRIX16 "", key);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max,
+                                int32_t fuzz, int32_t flat) {
+  ALOGV("ConfigureAbs 0x%" PRIX16 " %" PRId32 " %" PRId32 " %" PRId32
+        " %" PRId32 "",
+        abs_type, min, max, fuzz, flat);
+  if (abs_type < 0 || abs_type >= ABS_CNT) {
+    ALOGE("EV_ABS type 0x%" PRIX16 " out of range [0,0x%X)", abs_type, ABS_CNT);
+    return Error(ERROR_ABS_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = EnableEventType(EV_ABS)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_ABSBIT, abs_type)) {
+    ALOGE("failed to enable EV_ABS 0x%" PRIX16 "", abs_type);
+    return Error(status);
+  }
+  uidev_.absmin[abs_type] = min;
+  uidev_.absmax[abs_type] = max;
+  uidev_.absfuzz[abs_type] = fuzz;
+  uidev_.absflat[abs_type] = flat;
+  return 0;
+}
+
+int EvdevInjector::ConfigureMultiTouchXY(int x0, int y0, int x1, int y1) {
+  if (const int status = ConfigureAbs(ABS_MT_POSITION_X, x0, x1, 0, 0)) {
+    return status;
+  }
+  if (const int status = ConfigureAbs(ABS_MT_POSITION_Y, y0, y1, 0, 0)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureAbsSlots(int slots) {
+  return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
+}
+
+int EvdevInjector::ConfigureEnd() {
+  ALOGV("ConfigureEnd:");
+  ALOGV("  name=\"%s\"", uidev_.name);
+  ALOGV("  id.bustype=0x%04" PRIX16, uidev_.id.bustype);
+  ALOGV("  id.vendor=0x%04" PRIX16, uidev_.id.vendor);
+  ALOGV("  id.product=0x%04" PRIX16, uidev_.id.product);
+  ALOGV("  id.version=0x%04" PRIX16, uidev_.id.version);
+  ALOGV("  ff_effects_max=%" PRIu32, uidev_.ff_effects_max);
+  for (int i = 0; i < ABS_CNT; ++i) {
+    if (uidev_.absmin[i]) {
+      ALOGV("  absmin[%d]=%" PRId32, i, uidev_.absmin[i]);
+    }
+    if (uidev_.absmax[i]) {
+      ALOGV("  absmax[%d]=%" PRId32, i, uidev_.absmax[i]);
+    }
+    if (uidev_.absfuzz[i]) {
+      ALOGV("  absfuzz[%d]=%" PRId32, i, uidev_.absfuzz[i]);
+    }
+    if (uidev_.absflat[i]) {
+      ALOGV("  absflat[%d]=%" PRId32, i, uidev_.absflat[i]);
+    }
+  }
+
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  // Write out device settings.
+  if (const int status = uinput_->Write(&uidev_, sizeof uidev_)) {
+    ALOGE("failed to write device settings");
+    return Error(status);
+  }
+  // Create device node.
+  if (const int status = uinput_->IoctlVoid(UI_DEV_CREATE)) {
+    ALOGE("failed to create device node");
+    return Error(status);
+  }
+  state_ = State::READY;
+  return 0;
+}
+
+int EvdevInjector::Send(uint16_t type, uint16_t code, int32_t value) {
+  ALOGV("Send(0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32 ")", type, code, value);
+  if (const int status = RequireState(State::READY)) {
+    return status;
+  }
+  struct input_event event;
+  memset(&event, 0, sizeof(event));
+  event.type = type;
+  event.code = code;
+  event.value = value;
+  if (const int status = uinput_->Write(&event, sizeof(event))) {
+    ALOGE("failed to write event 0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32,
+          type, code, value);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::SendSynReport() { return Send(EV_SYN, SYN_REPORT, 0); }
+
+int EvdevInjector::SendKey(uint16_t code, int32_t value) {
+  return Send(EV_KEY, code, value);
+}
+
+int EvdevInjector::SendAbs(uint16_t code, int32_t value) {
+  return Send(EV_ABS, code, value);
+}
+
+int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
+  if (latest_slot_ != slot) {
+    if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
+      return status;
+    }
+    latest_slot_ = slot;
+  }
+  return 0;
+}
+
+int EvdevInjector::SendMultiTouchXY(int32_t slot, int32_t id, int32_t x,
+                                    int32_t y) {
+  if (const int status = SendMultiTouchSlot(slot)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_TRACKING_ID, id)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_POSITION_X, x)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_POSITION_Y, y)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::SendMultiTouchLift(int32_t slot) {
+  if (const int status = SendMultiTouchSlot(slot)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_TRACKING_ID, -1)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::Error(int code) {
+  if (!error_) {
+    error_ = code;
+  }
+  return code;
+}
+
+int EvdevInjector::RequireState(State required_state) {
+  if (error_) {
+    return error_;
+  }
+  if (state_ != required_state) {
+    ALOGE("in state %d but require state %d", static_cast<int>(state_),
+          static_cast<int>(required_state));
+    return Error(ERROR_SEQUENCING);
+  }
+  return 0;
+}
+
+int EvdevInjector::EnableEventType(uint16_t type) {
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (enabled_event_types_.count(type) > 0) {
+    return 0;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_EVBIT, type)) {
+    ALOGE("failed to enable event type 0x%X", type);
+    return Error(status);
+  }
+  enabled_event_types_.insert(type);
+  return 0;
+}
+
+void EvdevInjector::dumpInternal(String8& result) {
+  result.appendFormat("injector_state = %d\n", static_cast<int>(state_));
+  result.appendFormat("injector_error = %d\n", error_);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/EvdevInjector.h b/services/vr/virtual_touchpad/EvdevInjector.h
new file mode 100644
index 0000000..c69dbef
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -0,0 +1,142 @@
+#ifndef ANDROID_DVR_EVDEV_INJECTOR_H
+#define ANDROID_DVR_EVDEV_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <linux/uinput.h>
+#include <utils/String8.h>
+
+#include <cstdint>
+#include <memory>
+#include <unordered_set>
+
+namespace android {
+namespace dvr {
+
+// Simulated evdev input device.
+//
+class EvdevInjector {
+ public:
+  // EvdevInjector-specific error codes are negative integers; other non-zero
+  // values returned from public routines are |errno| codes from underlying I/O.
+  // EvdevInjector maintains a 'sticky' error state, similar to |errno|, so that
+  // a caller can perform a sequence of operations and check for errors at the
+  // end using |GetError()|. In general, the first such error will be recorded
+  // and will suppress effects of further device operations until |ResetError()|
+  // is called.
+  //
+  enum : int {
+    ERROR_DEVICE_NAME = -1,     // Invalid device name.
+    ERROR_PROPERTY_RANGE = -2,  // |INPUT_PROP_*| code out of range.
+    ERROR_KEY_RANGE = -3,       // |KEY_*|/|BTN_*| code out of range.
+    ERROR_ABS_RANGE = -4,       // |ABS_*| code out of range.
+    ERROR_SEQUENCING = -5,      // Configure/Send out of order.
+  };
+
+  // Key event |value| is not defined in <linux/input.h>.
+  enum : int32_t { KEY_RELEASE = 0, KEY_PRESS = 1, KEY_REPEAT = 2 };
+
+  // UInput provides a shim to intercept /dev/uinput operations
+  // just above the system call level, for testing.
+  //
+  class UInput {
+   public:
+    UInput() {}
+    virtual ~UInput() {}
+    virtual int Open();
+    virtual int Close();
+    virtual int Write(const void* buf, size_t count);
+    virtual int IoctlVoid(int request);
+    virtual int IoctlSetInt(int request, int value);
+
+   private:
+    base::unique_fd fd_;
+  };
+
+  EvdevInjector() {}
+  ~EvdevInjector() { Close(); }
+  void Close();
+
+  int GetError() const { return error_; }
+  void ResetError() { error_ = 0; }
+
+  // Configuration must be performed before sending any events.
+  // |ConfigureBegin()| must be called first, and |ConfigureEnd()| last,
+  // with zero or more other |Configure...()| calls in between in any order.
+
+  // Configure the basic evdev device properties; must be called first.
+  int ConfigureBegin(const char* device_name, int16_t bustype, int16_t vendor,
+                     int16_t product, int16_t version);
+
+  // Configure an optional input device property.
+  // @param property  One of the |INPUT_PROP_*| constants from <linux/input.h>.
+  int ConfigureInputProperty(int property);
+
+  // Configure an input key.
+  // @param key One of the |KEY_*| or |BTN_*| constants from <linux/input.h>.
+  int ConfigureKey(uint16_t key);
+
+  // Configure an absolute axis.
+  // @param abs_type One of the |KEY_*| or |BTN_*| constants from
+  // <linux/input.h>.
+  int ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max, int32_t fuzz,
+                   int32_t flat);
+
+  // Configure the number of multitouch slots.
+  int ConfigureAbsSlots(int slots);
+
+  // Configure multitouch coordinate range.
+  int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+
+  // Complete configuration and create the input device.
+  int ConfigureEnd();
+
+  // Send various events.
+  //
+  int Send(uint16_t type, uint16_t code, int32_t value);
+  int SendSynReport();
+  int SendKey(uint16_t code, int32_t value);
+  int SendAbs(uint16_t code, int32_t value);
+  int SendMultiTouchSlot(int32_t slot);
+  int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
+  int SendMultiTouchLift(int32_t slot);
+
+  void dumpInternal(String8& result);
+
+ protected:
+  // Must be called only between construction and ConfigureBegin().
+  inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
+  // Caller must not retain pointer longer than EvdevInjector.
+  inline const uinput_user_dev* GetUiDevForTesting() const { return &uidev_; }
+
+ private:
+  // Phase to enforce that configuration is complete before events are sent.
+  enum class State { NEW, CONFIGURING, READY, CLOSED };
+
+  // Sets |error_| if it is not already set; returns |code|.
+  int Error(int code);
+
+  // Returns a nonzero error if the injector is not in the required |state|.
+  int RequireState(State state);
+
+  // Configures an event type if necessary.
+  // @param type One of the |EV_*| constants from <linux/input.h>.
+  int EnableEventType(uint16_t type);
+
+  // Active pointer to owned or testing UInput.
+  UInput* uinput_ = nullptr;
+  std::unique_ptr<UInput> owned_uinput_;
+
+  State state_ = State::NEW;
+  int error_ = 0;
+  uinput_user_dev uidev_;
+  std::unordered_set<uint16_t> enabled_event_types_;
+  int32_t latest_slot_ = -1;
+
+  EvdevInjector(const EvdevInjector&) = delete;
+  void operator=(const EvdevInjector&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_EVDEV_INJECTOR_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
new file mode 100644
index 0000000..c7c8184
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
@@ -0,0 +1,80 @@
+#include "VirtualTouchpadClient.h"
+
+#include <android/dvr/IVirtualTouchpadService.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class VirtualTouchpadClientImpl : public VirtualTouchpadClient {
+ public:
+  VirtualTouchpadClientImpl() {}
+  ~VirtualTouchpadClientImpl() override {
+    if (service_ != nullptr) {
+      Detach();
+    }
+  }
+
+  status_t Attach() {
+    if (service_ != nullptr) {
+      return ALREADY_EXISTS;
+    }
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      ALOGE("no service manager");
+      return NO_INIT;
+    }
+    sp<IVirtualTouchpadService> service =
+        interface_cast<IVirtualTouchpadService>(
+            sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
+    if (service == nullptr) {
+      ALOGE("failed to get service");
+      return NAME_NOT_FOUND;
+    }
+    service_ = service;
+    return service_->attach().transactionError();
+  }
+
+  status_t Detach() {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    status_t status = service_->detach().transactionError();
+    service_ = nullptr;
+    return status;
+  }
+
+  status_t Touch(int touchpad, float x, float y, float pressure) override {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    return service_->touch(touchpad, x, y, pressure).transactionError();
+  }
+
+  status_t ButtonState(int touchpad, int buttons) override {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    return service_->buttonState(touchpad, buttons).transactionError();
+  }
+
+  void dumpInternal(String8& result) override {
+    result.append("[virtual touchpad]\n");
+    result.appendFormat("connected = %s\n\n",
+                        service_ != nullptr ? "true" : "false");
+  }
+
+ private:
+  sp<IVirtualTouchpadService> service_;
+};
+
+}  // anonymous namespace
+
+std::unique_ptr<VirtualTouchpad> VirtualTouchpadClient::Create() {
+  return std::unique_ptr<VirtualTouchpad>(new VirtualTouchpadClientImpl());
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
new file mode 100644
index 0000000..f0bdcd9
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -0,0 +1,186 @@
+#include "VirtualTouchpadEvdev.h"
+
+#include <android/input.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <log/log.h>
+
+// References:
+//  [0] Multi-touch (MT) Protocol,
+//      https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// Virtual evdev device properties. The name is arbitrary, but Android can
+// use it to look up device configuration, so it must be unique. Vendor and
+// product values must be 0 to indicate an internal device and prevent a
+// similar lookup that could conflict with a physical device.
+static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d";
+static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
+static constexpr int16_t kDeviceVendor = 0;
+static constexpr int16_t kDeviceProduct = 0;
+static constexpr int16_t kDeviceVersion = 0x0001;
+
+static constexpr int32_t kWidth = 0x10000;
+static constexpr int32_t kHeight = 0x10000;
+static constexpr int32_t kSlots = 2;
+
+}  // anonymous namespace
+
+std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
+  std::unique_ptr<VirtualTouchpadEvdev> touchpad(new VirtualTouchpadEvdev());
+  touchpad->Reset();
+  return touchpad;
+}
+
+void VirtualTouchpadEvdev::Reset() {
+  for (auto& touchpad : touchpad_) {
+    if (touchpad.injector) {
+      touchpad.injector->Close();
+    }
+    touchpad.injector = nullptr;
+    touchpad.owned_injector.reset();
+    touchpad.last_device_x = INT32_MIN;
+    touchpad.last_device_y = INT32_MIN;
+    touchpad.touches = 0;
+    touchpad.last_motion_event_buttons = 0;
+  }
+}
+
+status_t VirtualTouchpadEvdev::Attach() {
+  status_t status = OK;
+  for (int i = 0; i < kTouchpads; ++i) {
+    Touchpad& touchpad = touchpad_[i];
+    if (!touchpad.injector) {
+      touchpad.owned_injector.reset(new EvdevInjector());
+      touchpad.injector = touchpad.owned_injector.get();
+    }
+    String8 DeviceName;
+    DeviceName.appendFormat(kDeviceNameFormat, i);
+    touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
+                                      kDeviceVendor, kDeviceProduct,
+                                      kDeviceVersion);
+    touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
+    touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
+    touchpad.injector->ConfigureAbsSlots(kSlots);
+    touchpad.injector->ConfigureKey(BTN_TOUCH);
+    touchpad.injector->ConfigureKey(BTN_BACK);
+    touchpad.injector->ConfigureEnd();
+    if (const status_t configuration_status =  touchpad.injector->GetError()) {
+      status = configuration_status;
+    }
+  }
+  return status;
+}
+
+status_t VirtualTouchpadEvdev::Detach() {
+  Reset();
+  return OK;
+}
+
+int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
+                                float pressure) {
+  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+    return EINVAL;
+  }
+  if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
+    return EINVAL;
+  }
+  int32_t device_x = x * kWidth;
+  int32_t device_y = y * kHeight;
+  Touchpad& touchpad = touchpad_[touchpad_id];
+  touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
+  ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
+        device_y, touchpad.touches);
+
+  if (!touchpad.injector) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  touchpad.injector->ResetError();
+  switch (touchpad.touches) {
+    case 0b00:  // Hover continues.
+      if (device_x != touchpad.last_device_x ||
+          device_y != touchpad.last_device_y) {
+        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+        touchpad.injector->SendSynReport();
+      }
+      break;
+    case 0b01:  // Touch begins.
+      // Press.
+      touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
+      touchpad.injector->SendSynReport();
+      break;
+    case 0b10:  // Touch ends.
+      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+      touchpad.injector->SendMultiTouchLift(0);
+      touchpad.injector->SendSynReport();
+      break;
+    case 0b11:  // Touch continues.
+      if (device_x != touchpad.last_device_x ||
+          device_y != touchpad.last_device_y) {
+        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+        touchpad.injector->SendSynReport();
+      }
+      break;
+  }
+  touchpad.last_device_x = device_x;
+  touchpad.last_device_y = device_y;
+
+  return touchpad.injector->GetError();
+}
+
+int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
+  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+    return EINVAL;
+  }
+  Touchpad& touchpad = touchpad_[touchpad_id];
+  const int changes = touchpad.last_motion_event_buttons ^ buttons;
+  if (!changes) {
+    return 0;
+  }
+  if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
+    return ENOTSUP;
+  }
+  ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
+        buttons);
+
+  if (!touchpad.injector) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  touchpad.injector->ResetError();
+  if (changes & AMOTION_EVENT_BUTTON_BACK) {
+    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
+                                             ? EvdevInjector::KEY_PRESS
+                                             : EvdevInjector::KEY_RELEASE);
+    touchpad.injector->SendSynReport();
+  }
+  touchpad.last_motion_event_buttons = buttons;
+  return touchpad.injector->GetError();
+}
+
+void VirtualTouchpadEvdev::dumpInternal(String8& result) {
+  for (int i = 0; i < kTouchpads; ++i) {
+    const auto& touchpad = touchpad_[i];
+    result.appendFormat("[virtual touchpad %d]\n", i);
+    if (!touchpad.injector) {
+      result.append("injector = none\n");
+      return;
+    }
+    result.appendFormat("injector = %s\n",
+                        touchpad.owned_injector ? "normal" : "test");
+    result.appendFormat("touches = %d\n", touchpad.touches);
+    result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
+                        touchpad.last_device_x, touchpad.last_device_y);
+    result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
+                        touchpad.last_motion_event_buttons);
+    touchpad.injector->dumpInternal(result);
+    result.append("\n");
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
new file mode 100644
index 0000000..2fb8ff3
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -0,0 +1,67 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
+
+#include "EvdevInjector.h"
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+class EvdevInjector;
+
+// VirtualTouchpadEvdev implements a VirtualTouchpad by injecting evdev events.
+//
+class VirtualTouchpadEvdev : public VirtualTouchpad {
+ public:
+  static std::unique_ptr<VirtualTouchpad> Create();
+  ~VirtualTouchpadEvdev() override {}
+
+  // VirtualTouchpad implementation:
+  status_t Attach() override;
+  status_t Detach() override;
+  status_t Touch(int touchpad, float x, float y, float pressure) override;
+  status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
+
+ protected:
+  static constexpr int kTouchpads = 2;
+
+  VirtualTouchpadEvdev() {}
+  void Reset();
+
+  // Must be called only between construction (or Detach()) and Attach().
+  inline void SetEvdevInjectorForTesting(int touchpad,
+                                         EvdevInjector* injector) {
+    touchpad_[touchpad].injector = injector;
+  }
+
+ private:
+  // Per-touchpad state.
+  struct Touchpad {
+    // Except for testing, the |EvdevInjector| used to inject evdev events.
+    std::unique_ptr<EvdevInjector> owned_injector;
+
+    // Active pointer to |owned_injector_| or to a testing injector.
+    EvdevInjector* injector = nullptr;
+
+    // Previous (x, y) position in device space, to suppress redundant events.
+    int32_t last_device_x;
+    int32_t last_device_y;
+
+    // Records current touch state (0=up 1=down) in bit 0, and previous state
+    // in bit 1, to track transitions.
+    int touches;
+
+    // Previous injected button state, to detect changes.
+    int32_t last_motion_event_buttons;
+  };
+  Touchpad touchpad_[kTouchpads];
+
+  VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete;
+  void operator=(const VirtualTouchpadEvdev&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
new file mode 100644
index 0000000..81edd32
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -0,0 +1,135 @@
+#include "VirtualTouchpadService.h"
+
+#include <inttypes.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <linux/input.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kTouchPermission("android.permission.RESTRICTED_VR_ACCESS");
+}  // anonymous namespace
+
+VirtualTouchpadService::~VirtualTouchpadService() {
+  if (client_pid_) {
+    client_pid_ = 0;
+    touchpad_->Detach();
+  }
+}
+
+binder::Status VirtualTouchpadService::attach() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (client_pid_ == pid) {
+    // The same client has called attach() twice with no intervening detach().
+    // This indicates a problem with the client, so return an error.
+    // However, since the client is already attached, any touchpad actions
+    // it takes will still work.
+    ALOGE("pid=%ld attached twice", static_cast<long>(pid));
+    return binder::Status::fromStatusT(ALREADY_EXISTS);
+  }
+  if (client_pid_ != 0) {
+    // Attach while another client is attached. This can happen if the client
+    // dies without cleaning up after itself, so move ownership to the current
+    // caller. If two actual clients have connected, the problem will be
+    // reported when the previous client performs any touchpad action.
+    ALOGE("pid=%ld replaces %ld", static_cast<long>(pid),
+          static_cast<long>(client_pid_));
+    client_pid_ = pid;
+    return binder::Status::ok();
+  }
+  client_pid_ = pid;
+  if (const status_t error = touchpad_->Attach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::detach() {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  client_pid_ = 0;
+  if (const status_t error = touchpad_->Detach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::touch(int touchpad, float x, float y,
+                                             float pressure) {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->Touch(touchpad, x, y, pressure)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::buttonState(int touchpad, int buttons) {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->ButtonState(touchpad, buttons)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+status_t VirtualTouchpadService::dump(
+    int fd, const Vector<String16>& args[[gnu::unused]]) {
+  String8 result;
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
+  if ((uid != AID_SHELL) &&
+      !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+    result.appendFormat("Permission denial: can't dump " LOG_TAG
+                        " from pid=%ld, uid=%ld\n",
+                        static_cast<long>(pid), static_cast<long>(uid));
+  } else {
+    result.appendFormat("[service]\nclient_pid = %ld\n\n",
+                        static_cast<long>(client_pid_));
+    touchpad_->dumpInternal(result);
+  }
+  write(fd, result.string(), result.size());
+  return OK;
+}
+
+bool VirtualTouchpadService::CheckPermissions() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return false;
+  }
+  if (client_pid_ != pid) {
+    ALOGE("pid=%ld is not owner", static_cast<long>(pid));
+    return false;
+  }
+  return true;
+}
+
+bool VirtualTouchpadService::CheckTouchPermission(pid_t* out_pid) {
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  *out_pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
+  const bool permission = PermissionCache::checkPermission(kTouchPermission, *out_pid, uid);
+  if (!permission) {
+    ALOGE("permission denied to pid=%ld uid=%ld", static_cast<long>(*out_pid),
+          static_cast<long>(uid));
+  }
+  return permission;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
new file mode 100644
index 0000000..cf236f9
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -0,0 +1,46 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
+
+#include <android/dvr/BnVirtualTouchpadService.h>
+
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+// VirtualTouchpadService implements the service side of
+// the Binder interface defined in VirtualTouchpadService.aidl.
+//
+class VirtualTouchpadService : public BnVirtualTouchpadService {
+ public:
+  VirtualTouchpadService(std::unique_ptr<VirtualTouchpad> touchpad)
+      : touchpad_(std::move(touchpad)), client_pid_(0) {}
+  ~VirtualTouchpadService() override;
+
+ protected:
+  // Implements IVirtualTouchpadService.
+  binder::Status attach() override;
+  binder::Status detach() override;
+  binder::Status touch(int touchpad, float x, float y, float pressure) override;
+  binder::Status buttonState(int touchpad, int buttons) override;
+
+  // Implements BBinder::dump().
+  status_t dump(int fd, const Vector<String16>& args) override;
+
+ private:
+  bool CheckPermissions();
+  bool CheckTouchPermission(pid_t* out_pid);
+
+  std::unique_ptr<VirtualTouchpad> touchpad_;
+
+  // Only one client at a time can use the virtual touchpad.
+  pid_t client_pid_;
+
+  VirtualTouchpadService(const VirtualTouchpadService&) = delete;
+  void operator=(const VirtualTouchpadService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
new file mode 100644
index 0000000..9cfb186
--- /dev/null
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -0,0 +1,37 @@
+package android.dvr;
+
+/** @hide */
+interface VirtualTouchpadService
+{
+  const String SERVICE_NAME = "virtual_touchpad";
+
+  /**
+   * Initialize the virtual touchpad.
+   */
+  void attach() = 0;
+
+  /**
+   * Shut down the virtual touchpad.
+   */
+  void detach() = 1;
+
+  /**
+   * Generate a simulated touch event.
+   *
+   * @param touchpad Selects touchpad.
+   * @param x Horizontal touch position.
+   * @param y Vertical touch position.
+   * @param pressure Touch pressure; use 0.0 for no touch (lift or hover).
+   *
+   * Position values in the range [0.0, 1.0) map to the screen.
+   */
+  void touch(int touchpad, float x, float y, float pressure) = 2;
+
+  /**
+   * Generate a simulated touchpad button state event.
+   *
+   * @param touchpad Selects touchpad.
+   * @param buttons A union of MotionEvent BUTTON_* values.
+   */
+  void buttonState(int touchpad, int buttons) = 3;
+}
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpad.h b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
new file mode 100644
index 0000000..da3a0b7
--- /dev/null
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
@@ -0,0 +1,78 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
+
+#include "dvr/virtual_touchpad_client.h"
+
+#include <memory>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace dvr {
+
+// Provides a virtual touchpad for injecting events into the input system.
+//
+class VirtualTouchpad {
+ public:
+  enum : int {
+    PRIMARY = DVR_VIRTUAL_TOUCHPAD_PRIMARY,
+    VIRTUAL = DVR_VIRTUAL_TOUCHPAD_VIRTUAL,
+  };
+
+  virtual ~VirtualTouchpad() {}
+
+  // Create a virtual touchpad.
+  // Implementations should provide this, and hide their constructors.
+  // For the user, switching implementations should be as simple as changing
+  // the class whose |Create()| is called.
+  // Implementations should be minimial; major resource allocation should
+  // be performed in Attach().
+  static std::unique_ptr<VirtualTouchpad> Create() {
+    return nullptr;
+  }
+
+  // Initialize a virtual touchpad.
+  virtual status_t Attach() = 0;
+
+  // Shut down a virtual touchpad.
+  virtual status_t Detach() = 0;
+
+  // Generate a simulated touch event.
+  //
+  // @param touchpad Touchpad selector index.
+  // @param x Horizontal touch position.
+  // @param y Vertical touch position.
+  //            Values must be in the range [0.0, 1.0).
+  // @param pressure Touch pressure.
+  //            Positive values represent contact; use 1.0f if contact
+  //            is binary. Use 0.0f for no contact.
+  // @returns OK on success.
+  //
+  virtual status_t Touch(int touchpad, float x, float y, float pressure) = 0;
+
+  // Generate a simulated touchpad button state.
+  //
+  // @param touchpad Touchpad selector index.
+  // @param buttons A union of MotionEvent BUTTON_* values.
+  // @returns OK on success.
+  //
+  // Currently only BUTTON_BACK is supported, as the implementation
+  // restricts itself to operations actually required by VrWindowManager.
+  //
+  virtual status_t ButtonState(int touchpad, int buttons) = 0;
+
+  // Report state for 'dumpsys'.
+  virtual void dumpInternal(String8& result) = 0;
+
+ protected:
+  VirtualTouchpad() {}
+
+ private:
+  VirtualTouchpad(const VirtualTouchpad&) = delete;
+  void operator=(const VirtualTouchpad&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
new file mode 100644
index 0000000..23fb9f8
--- /dev/null
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
+
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+// VirtualTouchpadClient implements a VirtualTouchpad by connecting to
+// a VirtualTouchpadService over Binder.
+//
+class VirtualTouchpadClient : public VirtualTouchpad {
+ public:
+  // VirtualTouchpad implementation:
+  static std::unique_ptr<VirtualTouchpad> Create();
+  status_t Attach() override;
+  status_t Detach() override;
+  status_t Touch(int touchpad, float x, float y, float pressure) override;
+  status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
+
+ protected:
+  VirtualTouchpadClient() {}
+  ~VirtualTouchpadClient() override {}
+
+ private:
+  VirtualTouchpadClient(const VirtualTouchpadClient&) = delete;
+  void operator=(const VirtualTouchpadClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
diff --git a/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h b/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
new file mode 100644
index 0000000..08cca1b
--- /dev/null
+++ b/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_C_CLIENT_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_C_CLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
+
+enum {
+  DVR_VIRTUAL_TOUCHPAD_PRIMARY = 0,
+  DVR_VIRTUAL_TOUCHPAD_VIRTUAL = 1,
+};
+
+// Creates a new virtual touchpad client.
+//
+// @return Pointer to the created virtual touchpad client; nullptr on failure.
+//
+DvrVirtualTouchpad* dvrVirtualTouchpadCreate();
+
+// Destroys a virtual touchpad client.
+//
+// @param client Pointer to the virtual touchpad client to be destroyed.
+//
+void dvrVirtualTouchpadDestroy(DvrVirtualTouchpad* client);
+
+// Initialize the virtual touchpad.
+//
+// In the current server implementation, attachment creates and configures
+// the kernel virtual touchpad device(s). A single client may be attached
+// and detached repeatedly, e.g. on entering and leaving VR mode.
+//
+// @param client Pointer to the virtual touchpad client to be attached.
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadAttach(DvrVirtualTouchpad* client);
+
+// Shut down the virtual touchpad.
+//
+// @param client Pointer to the virtual touchpad client to be detached.
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadDetach(DvrVirtualTouchpad* client);
+
+// Generate a simulated touch event.
+//
+// @param client Pointer to the virtual touchpad client.
+// @param touchpad Selects touchpad.
+// @param x Horizontal touch position.
+// @param y Vertical touch position.
+// @param pressure Touch pressure; use 0.0 for no touch (lift or hover).
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadTouch(DvrVirtualTouchpad* client, int touchpad, float x,
+                            float y, float pressure);
+
+// Generate a simulated touchpad button state event.
+//
+// @param client Pointer to the virtual touchpad client.
+// @param touchpad Selects touchpad.
+// @param buttons A union of MotionEvent BUTTON_* values.
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
+                                  int buttons);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
diff --git a/services/vr/virtual_touchpad/main.cpp b/services/vr/virtual_touchpad/main.cpp
new file mode 100644
index 0000000..55ac9bf
--- /dev/null
+++ b/services/vr/virtual_touchpad/main.cpp
@@ -0,0 +1,33 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include "VirtualTouchpadEvdev.h"
+#include "VirtualTouchpadService.h"
+
+int main() {
+  ALOGI("Starting");
+  android::sp<android::dvr::VirtualTouchpadService> touchpad_service =
+      new android::dvr::VirtualTouchpadService(
+          android::dvr::VirtualTouchpadEvdev::Create());
+
+  signal(SIGPIPE, SIG_IGN);
+  android::sp<android::ProcessState> ps(android::ProcessState::self());
+  ps->setThreadPoolMaxThreadCount(4);
+  ps->startThreadPool();
+  ps->giveThreadPoolName();
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  const android::status_t service_status =
+      sm->addService(android::String16(touchpad_service->SERVICE_NAME()),
+                     touchpad_service, false /*allowIsolated*/);
+  if (service_status != android::OK) {
+    ALOGE("virtual touchpad service not added: %d",
+          static_cast<int>(service_status));
+    exit(2);
+  }
+
+  android::IPCThreadState::self()->joinThreadPool();
+  return 0;
+}
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
new file mode 100644
index 0000000..564bcd7
--- /dev/null
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -0,0 +1,327 @@
+#include <android/input.h>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "EvdevInjector.h"
+#include "VirtualTouchpadEvdev.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class UInputForTesting : public EvdevInjector::UInput {
+ public:
+  ~UInputForTesting() override {}
+  void WriteInputEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event;
+    memset(&event, 0, sizeof(event));
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    Write(&event, sizeof(event));
+  }
+};
+
+// Recording test implementation of UInput.
+//
+class UInputRecorder : public UInputForTesting {
+ public:
+  UInputRecorder() {}
+  ~UInputRecorder() override {}
+
+  const std::string& GetString() const { return s_; }
+  void Reset() { s_.clear(); }
+
+  // UInput overrides:
+
+  int Open() override {
+    s_ += "o;";
+    return 0;
+  }
+
+  int Close() override {
+    s_ += "c;";
+    return 0;
+  }
+
+  int Write(const void* buf, size_t count) override {
+    s_ += "w(";
+    s_ += Encode(&count, sizeof(count));
+    s_ += ",";
+    s_ += Encode(buf, count);
+    s_ += ");";
+    return 0;
+  }
+
+  int IoctlVoid(int request) override {
+    s_ += "i(";
+    s_ += Encode(&request, sizeof(request));
+    s_ += ");";
+    return 0;
+  }
+
+  int IoctlSetInt(int request, int value) override {
+    s_ += "i(";
+    s_ += Encode(&request, sizeof(request));
+    s_ += ",";
+    s_ += Encode(&value, sizeof(value));
+    s_ += ");";
+    return 0;
+  }
+
+ private:
+  std::string s_;
+
+  std::string Encode(const void* buf, size_t count) {
+    const char* in = static_cast<const char*>(buf);
+    char out[2 * count + 1];
+    for (size_t i = 0; i < count; ++i) {
+      snprintf(&out[2 * i], 3, "%02X", in[i]);
+    }
+    return out;
+  }
+};
+
+class EvdevInjectorForTesting : public EvdevInjector {
+ public:
+  EvdevInjectorForTesting() { SetUInputForTesting(&record); }
+  const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
+  UInputRecorder record;
+};
+
+class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
+ public:
+  static std::unique_ptr<VirtualTouchpad> Create() {
+    return std::unique_ptr<VirtualTouchpad>(New());
+  }
+  static VirtualTouchpadForTesting* New() {
+    VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
+    touchpad->Reset();
+    for (int t = 0; t < kTouchpads; ++t) {
+      touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]);
+    }
+    return touchpad;
+  }
+  int GetTouchpadCount() const { return kTouchpads; }
+  EvdevInjectorForTesting injector[kTouchpads];
+};
+
+void DumpDifference(const char* expect, const char* actual) {
+  printf("  common: ");
+  while (*expect && *expect == *actual) {
+    putchar(*expect);
+    ++expect;
+    ++actual;
+  }
+  printf("\n  expect: %s\n", expect);
+  printf("  actual: %s\n", actual);
+}
+
+}  // anonymous namespace
+
+class VirtualTouchpadTest : public testing::Test {};
+
+TEST_F(VirtualTouchpadTest, Goodness) {
+  std::unique_ptr<VirtualTouchpadForTesting> touchpad(
+      VirtualTouchpadForTesting::New());
+  UInputRecorder expect;
+
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
+
+  // Check some aspects of uinput_user_dev.
+  const uinput_user_dev* uidev;
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    uidev = touchpad->injector[t].GetUiDev();
+    String8 name;
+    name.appendFormat("vr-virtual-touchpad-%d", t);
+    EXPECT_EQ(name, uidev->name);
+    for (int i = 0; i < ABS_CNT; ++i) {
+      EXPECT_EQ(0, uidev->absmin[i]);
+      EXPECT_EQ(0, uidev->absfuzz[i]);
+      EXPECT_EQ(0, uidev->absflat[i]);
+      if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y &&
+          i != ABS_MT_SLOT) {
+        EXPECT_EQ(0, uidev->absmax[i]);
+      }
+    }
+  }
+  const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
+  const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
+  const int32_t slots = uidev->absmax[ABS_MT_SLOT];
+
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    // Check the system calls performed by initialization.
+    expect.Reset();
+    // From ConfigureBegin():
+    expect.Open();
+    // From ConfigureInputProperty(INPUT_PROP_DIRECT):
+    expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    // From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
+    expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+    // From ConfigureAbsSlots(kSlots):
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
+    // From ConfigureKey(BTN_TOUCH):
+    expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
+    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
+    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
+    // From ConfigureEnd():
+    expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
+    expect.IoctlVoid(UI_DEV_CREATE);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0, 0, 0);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
+  expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f);
+    EXPECT_EQ(EINVAL, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, 0);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
+  expect.Close();
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+  }
+  touch_status = touchpad->Detach();
+  EXPECT_EQ(0, touch_status);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+}
+
+TEST_F(VirtualTouchpadTest, Badness) {
+  std::unique_ptr<VirtualTouchpadForTesting> touchpad(
+      VirtualTouchpadForTesting::New());
+  UInputRecorder expect;
+  UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;
+
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
+
+  // Touch off-screen should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 1.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Unsupported button should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
+                                       AMOTION_EVENT_BUTTON_FORWARD);
+  EXPECT_NE(OK, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Repeated attach is an error.
+  touch_status = touchpad->Attach();
+  EXPECT_NE(0, touch_status);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
new file mode 100644
index 0000000..99315ef
--- /dev/null
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -0,0 +1,5 @@
+service virtual_touchpad /system/bin/virtual_touchpad
+  class core
+  user system
+  group system input
+  writepid /dev/cpuset/system/tasks
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
new file mode 100644
index 0000000..91c270e
--- /dev/null
+++ b/vulkan/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libvulkan_headers",
+    from: "include",
+    to: "",
+    srcs: [
+        "include/vulkan/vk_platform.h",
+        "include/vulkan/vulkan.h",
+    ],
+    license: "include/vulkan/NOTICE",
+}
+
+cc_library_headers {
+    name: "vulkan_headers",
+    export_include_dirs: ["include"],
+    vendor_available: true,
+}
+
+subdirs = [
+    "nulldrv",
+    "libvulkan",
+    "tools",
+]
diff --git a/vulkan/Android.mk b/vulkan/Android.mk
deleted file mode 100644
index d125673..0000000
--- a/vulkan/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-named-subdir-makefiles, libvulkan nulldrv tools)
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
index 7aa19e7..eb0124d 100644
--- a/vulkan/api/platform.api
+++ b/vulkan/api/platform.api
@@ -48,3 +48,10 @@
 // VK_USE_PLATFORM_WIN32_KHR
 @internal type void* HINSTANCE
 @internal type void* HWND
+@internal type void* HANDLE
+@internal type u32   DWORD
+@internal type u16*  LPCWSTR
+@internal class SECURITY_ATTRIBUTES {}
+
+// VK_USE_PLATFORM_XLIB_XRANDR_EXT
+@internal type u64 RROutput
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 870f8eb..a19fcf1 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
 // API version (major.minor.patch)
 define VERSION_MAJOR 1
 define VERSION_MINOR 0
-define VERSION_PATCH 13
+define VERSION_PATCH 46
 
 // API limits
 define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -37,6 +37,9 @@
 define VK_MAX_DESCRIPTION_SIZE          256
 define VK_MAX_MEMORY_TYPES              32
 define VK_MAX_MEMORY_HEAPS              16    /// The maximum number of unique memory heaps, each of which supporting 1 or more memory types.
+define VK_MAX_DEVICE_GROUP_SIZE_KHX     32
+define VK_LUID_SIZE_KHX                 8
+define VK_QUEUE_FAMILY_EXTERNAL_KHX     -2
 
 // API keywords
 define VK_TRUE        1
@@ -45,57 +48,289 @@
 // API keyword, but needs special handling by some templates
 define NULL_HANDLE 0
 
+// 1
 @extension("VK_KHR_surface") define VK_KHR_SURFACE_SPEC_VERSION                 25
 @extension("VK_KHR_surface") define VK_KHR_SURFACE_EXTENSION_NAME               "VK_KHR_surface"
 
+// 2
 @extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_SPEC_VERSION             68
 @extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_EXTENSION_NAME           "VK_KHR_swapchain"
 
+// 3
 @extension("VK_KHR_display") define VK_KHR_DISPLAY_SPEC_VERSION                 21
 @extension("VK_KHR_display") define VK_KHR_DISPLAY_EXTENSION_NAME               "VK_KHR_display"
 
+// 4
 @extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION     9
 @extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME   "VK_KHR_display_swapchain"
 
+// 5
 @extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_SPEC_VERSION       6
 @extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_NAME               "VK_KHR_xlib_surface"
 
+// 6
 @extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_SPEC_VERSION         6
 @extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_NAME                 "VK_KHR_xcb_surface"
 
-@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 5
+// 7
+@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
 @extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_NAME         "VK_KHR_wayland_surface"
 
+// 8
 @extension("VK_KHR_mir_surface") define VK_KHR_MIR_SURFACE_SPEC_VERSION         4
 @extension("VK_KHR_mir_surface") define VK_KHR_MIR_SURFACE_NAME                 "VK_KHR_mir_surface"
 
+// 9
 @extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6
 @extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_NAME         "VK_KHR_android_surface"
 
+// 10
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION     5
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+// 11
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     7
 @extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
 
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       2
+// 12
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       6
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
 
+// 13
 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION           1
 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME                   "VK_NV_glsl_shader"
 
+// 15
 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION   1
 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME           "VK_KHR_sampler_mirror_clamp_to_edge"
 
+// 16
 @extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_SPEC_VERSION       1
 @extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_NAME               "VK_IMG_filter_cubic"
 
+// 19
 @extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION   1
 @extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_NAME           "VK_AMD_rasterization_order"
 
-@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       3
+// 21
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+// 22
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
+// 23
+@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       4
 @extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME               "VK_EXT_debug_marker"
 
+// 26
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_SPEC_VERSION 1
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
+
+// 27
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+// 28
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+// 34
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+// 36
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+// 37
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+// 38
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+// 54
+@extension("VK_KHX_multiview") define VK_KHX_MULTIVIEW_SPEC_VERSION 1
+@extension("VK_KHX_multiview") define VK_KHX_MULTIVIEW_EXTENSION_NAME "VK_KHX_multiview"
+
+// 56
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+// 57
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+// 58
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+// 59
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+// 60
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+// 61
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+
+// 62
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+// 63
+@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_SPEC_VERSION 1
+@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface"
+
+// 64
+@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
+@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
+
+// 65
+@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
+@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
+
+// 66
+@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
+@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
+
+// 70
+@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_SPEC_VERSION 1
+@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
+
+// 71
+@extension("VK_KHX_device_group_creation") define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1
+@extension("VK_KHX_device_group_creation") define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation"
+
+// 72
+@extension("VK_KHX_external_memory_capabilities") define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_capabilities") define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities"
+
+// 73
+@extension("VK_KHX_external_memory") define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1
+@extension("VK_KHX_external_memory") define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory"
+
+// 74
+@extension("VK_KHX_external_memory_win32") define VK_KHX_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_win32") define VK_KHX_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHX_external_memory_win32"
+
+// 75
+@extension("VK_KHX_external_memory_fd") define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_fd") define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd"
+
+// 76
+@extension("VK_KHX_win32_keyed_mutex") define VK_KHX_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+@extension("VK_KHX_win32_keyed_mutex") define VK_KHX_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHX_win32_keyed_mutex"
+
+// 77
+@extension("VK_KHX_external_semaphore_capabilities") define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_capabilities") define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities"
+
+// 78
+@extension("VK_KHX_external_semaphore") define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore") define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore"
+
+// 79
+@extension("VK_KHX_external_semaphore_win32") define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_win32") define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHX_external_semaphore_win32"
+
+// 80
+@extension("VK_KHX_external_semaphore_fd") define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_fd") define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd"
+
+// 81
+@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1
+@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
+
+// 85
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+// 86
+@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
+@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
+
+// 87
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+// 88
+@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
+@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
+
+// 89
+@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
+@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
+
+// 90
+@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
+@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
+
+// 91
+@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
+@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+
+// 92
+@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
+@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_COUNTER_EXTENSION_NAME "VK_EXT_display_control"
+
+// 93
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+// 95
+@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
+@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
+
+// 96
+@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
+@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
+
+// 97
+@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
+@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
+
+// 98
+@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
+@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
+
+// 99
+@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
+@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
+
+// 100
+@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+// 105
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 2
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
+
+// 106
+@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_SPEC_VERSION 1
+@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
+
+// 112
+@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
+@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
+
+// 119
+@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
+@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
+
+// 123
+@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_SPEC_VERSION 1
+@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
+
+// 124
+@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_SPEC_VERSION 1
+@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
 
 /////////////
 //  Types  //
@@ -135,15 +370,26 @@
 @nonDispatchHandle type u64 VkRenderPass
 @nonDispatchHandle type u64 VkPipelineCache
 
+// 1
 @extension("VK_KHR_surface")    @nonDispatchHandle type u64 VkSurfaceKHR
 
+// 2
 @extension("VK_KHR_swapchain")  @nonDispatchHandle type u64 VkSwapchainKHR
 
+// 3
 @extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayKHR
 @extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayModeKHR
 
+// 12
 @extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT
 
+// 86
+@extension("VK_KHR_descriptor_update_template") @nonDispatchHandle type u64 VkDescriptorUpdateTemplateKHR
+
+// 87
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
+
 
 /////////////
 //  Enums  //
@@ -160,8 +406,11 @@
     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL                    = 0x00000007,   /// Optimal layout when image is used only as destination of transfer operations
     VK_IMAGE_LAYOUT_PREINITIALIZED                          = 0x00000008,   /// Initial layout used when the data is populated by the CPU
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR                         = 1000001002,
+
+    //@extension("VK_KHR_shared_presentable_image")
+    VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR                      = 1000111000,
 }
 
 enum VkAttachmentLoadOp {
@@ -273,7 +522,7 @@
     VK_FILTER_NEAREST                                       = 0x00000000,
     VK_FILTER_LINEAR                                        = 0x00000001,
 
-    //@extension("VK_IMG_filter_cubic")
+    //@extension("VK_IMG_filter_cubic") // 16
     VK_FILTER_CUBIC_IMG                                     = 1000015000,
 }
 
@@ -584,6 +833,16 @@
     VK_FORMAT_ASTC_12x10_SRGB_BLOCK                         = 182,
     VK_FORMAT_ASTC_12x12_UNORM_BLOCK                        = 183,
     VK_FORMAT_ASTC_12x12_SRGB_BLOCK                         = 184,
+
+    //@extension("VK_IMG_format_pvrtc") // 28
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG                   = 1000054000,
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG                   = 1000054001,
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG                   = 1000054002,
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG                   = 1000054003,
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG                    = 1000054004,
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG                    = 1000054005,
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG                    = 1000054006,
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG                    = 1000054007,
 }
 
 /// Structure type enumerant
@@ -638,52 +897,208 @@
     VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO               = 47,
     VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO                 = 48,
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR                 = 1000001000,
     VK_STRUCTURE_TYPE_PRESENT_INFO_KHR                          = 1000001001,
 
-    //@extension("VK_KHR_display")
+    //@extension("VK_KHR_display") // 3
     VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR              = 1000002000,
     VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR           = 1000002001,
 
-    //@extension("VK_KHR_display_swapchain")
+    //@extension("VK_KHR_display_swapchain") // 4
     VK_STRUCTURE_TYPE_DISPLAY_DISPLAY_PRESENT_INFO_KHR          = 1000003000,
 
-    //@extension("VK_KHR_xlib_surface")
+    //@extension("VK_KHR_xlib_surface") // 5
     VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR              = 1000004000,
 
-    //@extension("VK_KHR_xcb_surface")
+    //@extension("VK_KHR_xcb_surface") // 6
     VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR               = 1000005000,
 
-    //@extension("VK_KHR_wayland_surface")
+    //@extension("VK_KHR_wayland_surface") // 7
     VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR           = 1000006000,
 
-    //@extension("VK_KHR_mir_surface")
+    //@extension("VK_KHR_mir_surface") // 8
     VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR               = 1000007000,
 
-    //@extension("VK_KHR_android_surface")
+    //@extension("VK_KHR_android_surface") // 9
     VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR           = 1000008000,
 
-    //@extension("VK_KHR_win32_surface")
+    //@extension("VK_KHR_win32_surface") // 10
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR             = 1000009000,
 
-    //@extension("VK_ANDROID_native_buffer")
+    //@extension("VK_ANDROID_native_buffer") // 11
     VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID                     = 1000010000,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID       = 1000010001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID = 1000010002,
 
-    //@extension("VK_EXT_debug_report")
+    //@extension("VK_EXT_debug_report") // 12
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT     = 1000011000,
 
-    //@extension("VK_AMD_rasterization_order")
+    //@extension("VK_AMD_rasterization_order") // 19
     VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000,
 
-    //@extension("VK_EXT_debug_marker")
+    //@extension("VK_EXT_debug_marker") // 23
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT         = 1000022000,
-
-    //@extension("VK_EXT_debug_marker")
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT          = 1000022001,
-
-    //@extension("VK_EXT_debug_marker")
     VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT              = 1000022002,
+
+    //@extension("VK_NV_dedicated_allocation") // 27
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+
+    //@extension("VK_KHX_multiview") // 54
+    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX     = 1000053000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX    = 1000053001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX  = 1000053002,
+
+    //@extension("VK_NV_external_memory") // 57
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV      = 1000056000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV            = 1000056001,
+
+    //@extension("VK_NV_external_memory_win32") // 58
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057001,
+
+    //@extension("VK_NV_win32_keyed_mutex") // 59
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+
+    //@extension("VK_KHR_get_physical_device_properties2") // 60
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR            = 1000059000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR          = 1000059001,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR                   = 1000059002,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR             = 1000059003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR   = 1000059004,
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR             = 1000059005,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR   = 1000059006,
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR      = 1000059007,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX            = 1000060000,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX               = 1000060001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX                = 1000060002,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX   = 1000060003,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX              = 1000060005,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX         = 1000060006,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX     = 1000060007,
+    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX           = 1000060008,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX      = 1000060009,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX               = 1000060010,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX             = 1000060011,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX    = 1000060012,
+
+    //@extension("VK_EXT_validation_flags") // 62
+    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT                      = 1000061000,
+
+    //@extension("VK_NN_vi_surface") // 63
+    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN                 = 1000062000,
+
+    //@extension("VK_KHX_device_group_creation") // 71
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX      = 1000070000,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX       = 1000070001,
+
+    //@extension("VK_KHX_external_memory_capabilities") // 72
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX    = 1000071000,
+    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX      = 1000071001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX  = 1000071002,
+    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX            = 1000071003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX         = 1000071004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHX          = 1000071005,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHX             = 1000071006,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHX   = 1000071007,
+
+    //@extension("VK_KHX_external_memory") // 73
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX    = 1000072000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX     = 1000072001,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX           = 1000072002,
+
+    //@extension("VK_KHX_external_memory_win32") // 74
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX       = 1000073000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX       = 1000073001,
+    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX        = 1000073002,
+
+    //@extension("VK_KHX_external_memory_fd") // 75
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX                 = 1000074000,
+    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX                  = 1000074001,
+
+    //@extension("VK_KHX_win32_keyed_mutex") // 76
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX    = 1000075000,
+
+    //@extension("VK_KHX_external_semaphore_capabilities") // 77
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX   = 1000076000,
+    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX         = 1000076001,
+
+    //@extension("VK_KHX_external_semaphore") // 78
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX          = 1000077000,
+
+    //@extension("VK_KHX_external_semaphore_win32") // 79
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX    = 1000078000,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX    = 1000078001,
+    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX               = 1000078002,
+
+    //@extension("VK_KHX_external_semaphore_fd") // 80
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX              = 1000079000,
+
+    //@extension("VK_KHR_push_descriptor") // 81
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR    = 1000080000,
+
+    //@extension("VK_KHR_incremental_present") // 85
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR                       = 1000084000,
+
+    //@extension("VK_KHR_descriptor_update_template") // 86
+    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR    = 1000085000,
+
+    //@extension("VK_NVX_device_generated_commands") // 87
+    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX              = 1000086000,
+    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX  = 1000086001,
+    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX             = 1000086002,
+    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX   = 1000086003,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX      = 1000086004,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX    = 1000086005,
+
+    //@extension("VK_NV_clip_space_w_scaling") // 88
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV  = 1000087000,
+
+    //@extension("VK_EXT_display_surface_counter") // 91
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT                 = 1000090000,
+
+    //@extension("VK_EXT_display_control") // 92
+    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT                    = 1000091000,
+    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT                     = 1000091001,
+    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT                    = 1000091002,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT         = 1000091003,
+
+    //@extension("VK_GOOGLE_display_timing") // 93
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE                 = 1000092000,
+
+    //@extension("VK_NVX_multiview_per_view_attributes") // 98
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX  = 1000097000,
+
+    //@extension("VK_NV_viewport_swizzle") // 99
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV    = 1000098000,
+
+    //@extension("VK_EXT_discard_rectangles") // 100
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT  = 1000099000,
+    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT  = 1000099001,
+
+    //@extension("VK_EXT_hdr_metadata") // 106
+    VK_STRUCTURE_TYPE_HDR_METADATA_EXT                          = 1000105000,
+
+    //@extension("VK_KHR_shared_presentable_image") // 111
+    VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR   = 1000111000,
+
+    //@extension("VK_KHR_get_surface_capabilities2") // 119
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR        = 1000119000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR                = 1000119001,
+    VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR                      = 1000119002,
+
+    //@extension("VK_MVK_ios_surface") // 123
+    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK               = 1000122000,
+
+    //@extension("VK_MVK_macos_surface") // 124
+    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK             = 1000123000,
 }
 
 enum VkSubpassContents {
@@ -706,7 +1121,7 @@
     VK_EVENT_RESET                                          = 4,
     VK_INCOMPLETE                                           = 5,
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_SUBOPTIMAL_KHR                                       = 1000001003,
 
     // Error codes (negative values)
@@ -721,24 +1136,29 @@
     VK_ERROR_INCOMPATIBLE_DRIVER                            = 0xFFFFFFF7, // -9
     VK_ERROR_TOO_MANY_OBJECTS                               = 0xFFFFFFF6, // -10
     VK_ERROR_FORMAT_NOT_SUPPORTED                           = 0xFFFFFFF5, // -11
+    VK_ERROR_FRAGMENTED_POOL                                = 0xFFFFFFF4, // -12
 
-    //@extension("VK_KHR_surface")
+    //@extension("VK_KHR_surface") // 1
     VK_ERROR_SURFACE_LOST_KHR                               = 0xC4653600, // -1000000000
+    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR                       = 0xC46535FF, // -1000000001
 
-    //@extension("VK_KHR_surface")
-    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR                       = 0xC46535FF, // -1000008001
-
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_ERROR_OUT_OF_DATE_KHR                                = 0xC4653214, // -1000001004
 
-    //@extension("VK_KHR_display_swapchain")
+    //@extension("VK_KHR_display_swapchain") // 4
     VK_ERROR_INCOMPATIBLE_DISPLAY_KHR                       = 0xC4652A47, // -1000003001
 
-    //@extension("VK_EXT_debug_report")
+    //@extension("VK_EXT_debug_report") // 12
     VK_ERROR_VALIDATION_FAILED_EXT                          = 0xC4650B07, // -1000011001
 
-    //@extension("VK_NV_glsl_shader")
+    //@extension("VK_NV_glsl_shader") // 13
     VK_ERROR_INVALID_SHADER_NV                              = 0xC4650720, // -1000012000
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_ERROR_OUT_OF_POOL_MEMORY_KHR                         = 0xC4642878, // -1000069000
+
+    //@extension("VK_KHX_external_memory") // 73
+    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX                    = 0xC4641CBD, // -1000072003
 }
 
 enum VkDynamicState {
@@ -751,22 +1171,47 @@
     VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK                   = 0x00000006,
     VK_DYNAMIC_STATE_STENCIL_WRITE_MASK                     = 0x00000007,
     VK_DYNAMIC_STATE_STENCIL_REFERENCE                      = 0x00000008,
+
+    //@extension("VK_NV_clip_space_w_scaling") // 88
+    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV                  = 1000087000,
+
+    //@extension("VK_EXT_discard_rectangles") // 100
+    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT                  = 1000099000,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 enum VkPresentModeKHR {
     VK_PRESENT_MODE_IMMEDIATE_KHR                           = 0x00000000,
     VK_PRESENT_MODE_MAILBOX_KHR                             = 0x00000001,
     VK_PRESENT_MODE_FIFO_KHR                                = 0x00000002,
     VK_PRESENT_MODE_FIFO_RELAXED_KHR                        = 0x00000003,
+
+    //@extension("VK_KHR_shared_presentable_image")
+    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR               = 1000111000,
+    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR           = 1000111001,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 enum VkColorSpaceKHR {
     VK_COLORSPACE_SRGB_NONLINEAR_KHR                        = 0x00000000,
+
+    //@extension("VK_EXT_swapchain_colorspace") // 105
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT                 = 1000104001,
+    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT                 = 1000104002,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT                        = 1000104003,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT                     = 1000104004,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT                         = 1000104005,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT                      = 1000104006,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT                        = 1000104007,
+    VK_COLOR_SPACE_HDR10_ST2084_EXT                         = 1000104008,
+    VK_COLOR_SPACE_DOLBYVISION_EXT                          = 1000104009,
+    VK_COLOR_SPACE_HDR10_HLG_EXT                            = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT                      = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT                   = 1000104012,
+    VK_COLOR_SPACE_PASS_THROUGH_EXT                         = 1000104013,
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 enum VkDebugReportObjectTypeEXT {
     VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT                 = 0,
     VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT                = 1,
@@ -797,20 +1242,93 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT             = 26,
     VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT           = 27,
     VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT            = 28,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT             = 29,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT        = 30,
+    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT        = 31,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+
+    //extension("VK_KHR_descriptor_update_template") // 86
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 enum VkDebugReportErrorEXT {
     VK_DEBUG_REPORT_ERROR_NONE_EXT                          = 0,
     VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT                  = 1,
 }
 
-@extension("VK_AMD_rasterization_order")
+@extension("VK_AMD_rasterization_order") // 19
 enum VkRasterizationOrderAMD {
     VK_RASTERIZATION_ORDER_STRICT_AMD                       = 0,
     VK_RASTERIZATION_ORDER_RELAXED_AMD                      = 1,
 }
 
+@extension("VK_EXT_validation_flags") // 62
+enum VkValidationCheckEXT {
+    VK_VALIDATION_CHECK_ALL_EXT                             = 0,
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+enum VkDescriptorUpdateTemplateTypeKHR {
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR   = 0,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+enum VkIndirectCommandsTokenTypeNVX {
+    VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX                 = 0,
+    VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX           = 1,
+    VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX             = 2,
+    VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX            = 3,
+    VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX            = 4,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX             = 5,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX                     = 6,
+    VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX                 = 7,
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+enum VkObjectEntryTypeNVX {
+    VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX                      = 0,
+    VK_OBJECT_ENTRY_PIPELINE_NVX                            = 1,
+    VK_OBJECT_ENTRY_INDEX_BUFFER_NVX                        = 2,
+    VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX                       = 3,
+    VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX                       = 4,
+}
+
+@extension("VK_EXT_display_control") // 92
+enum VkDisplayPowerStateEXT {
+    VK_DISPLAY_POWER_STATE_OFF_EXT                          = 0,
+    VK_DISPLAY_POWER_STATE_SUSPEND_EXT                      = 1,
+    VK_DISPLAY_POWER_STATE_ON_EXT                           = 2,
+}
+
+@extension("VK_EXT_display_control") // 92
+enum VkDeviceEventTypeEXT {
+    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT                = 0,
+}
+
+@extension("VK_EXT_display_control") // 92
+enum VkDisplayEventTypeEXT {
+    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT               = 0,
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+enum VkViewportCoordinateSwizzleNV {
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV            = 0,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV            = 1,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV            = 2,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV            = 3,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV            = 4,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV            = 5,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV            = 6,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV            = 7,
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+}
 
 /////////////////
 //  Bitfields  //
@@ -839,6 +1357,9 @@
 type VkFlags VkMemoryHeapFlags
 bitfield VkMemoryHeapFlagBits {
     VK_MEMORY_HEAP_DEVICE_LOCAL_BIT                         = 0x00000001,
+
+    //@extension("VK_KHX_device_group_creation") // 71
+    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX                   = 0x00000002,
 }
 
 /// Access flags
@@ -861,6 +1382,10 @@
     VK_ACCESS_HOST_WRITE_BIT                                = 0x00004000,
     VK_ACCESS_MEMORY_READ_BIT                               = 0x00008000,
     VK_ACCESS_MEMORY_WRITE_BIT                              = 0x00010000,
+
+    //@extension("VK_NVX_device_generated_commands") // 87
+    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX                  = 0x00020000,
+    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX                 = 0x00040000,
 }
 
 /// Buffer usage flags
@@ -931,6 +1456,12 @@
     VK_IMAGE_CREATE_SPARSE_ALIASED_BIT                      = 0x00000004,    /// Image should support constent data access to physical memory blocks mapped into multiple locations of sparse images
     VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT                      = 0x00000008,    /// Allows image views to have different format than the base image
     VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT                     = 0x00000010,    /// Allows creating image views with cube type from the created image
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR             = 0x00000020,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_IMAGE_CREATE_BIND_SFR_BIT_KHX                        = 0x00000040,
 }
 
 /// Image view creation flags
@@ -944,6 +1475,10 @@
     VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT             = 0x00000001,
     VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT                = 0x00000002,
     VK_PIPELINE_CREATE_DERIVATIVE_BIT                       = 0x00000004,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008,
+    VK_PIPELINE_CREATE_DISPATCH_BASE_KHX                    = 0x00000010,
 }
 
 /// Color component flags
@@ -983,8 +1518,12 @@
     VK_FORMAT_FEATURE_BLIT_DST_BIT                          = 0x00000800,    /// Format can be used as the destination image of blits with vkCommandBlitImage
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT       = 0x00001000,
 
-    //@extension("VK_IMG_filter_cubic")
+    //@extension("VK_IMG_filter_cubic") // 16
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG    = 0x00002000,
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR                  = 0x00004000,
+    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR                  = 0x00008000,
 }
 
 /// Query control flags
@@ -1085,6 +1624,9 @@
 
     VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT                      = 0x00008000,  /// All stages of the graphics pipeline
     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT                      = 0x00010000,  /// All graphics, compute, copy, and transition commands
+
+    //@extension("VK_NVX_device_generated_commands") // 87
+    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX               = 0x00020000,
 }
 
 /// Render pass attachment description flags
@@ -1096,6 +1638,9 @@
 /// Subpass description flags
 type VkFlags VkSubpassDescriptionFlags
 bitfield VkSubpassDescriptionFlagBits {
+    //@extension("VK_NVX_multiview_per_view_attributes") // 98
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX      = 0x00000001,
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
 }
 
 /// Command pool creation flags
@@ -1171,8 +1716,10 @@
 
 /// Descriptor set layout creation flags
 type VkFlags VkDescriptorSetLayoutCreateFlags
-//bitfield VkDescriptorSetLayoutCreateFlagBits {
-//}
+bitfield VkDescriptorSetLayoutCreateFlagBits {
+    //@extension("VK_KHR_push_descriptor") // 81
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR     = 0x00000001,
+}
 
 /// Pipeline vertex input state creation flags
 type VkFlags VkPipelineVertexInputStateCreateFlags
@@ -1243,6 +1790,12 @@
 type VkFlags VkDependencyFlags
 bitfield VkDependencyFlagBits {
     VK_DEPENDENCY_BY_REGION_BIT                             = 0x00000001,
+
+    //@extension("VK_KHX_multiview") // 54
+    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX                        = 0x00000002,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX                      = 0x00000004,
 }
 
 /// Cull mode flags
@@ -1254,9 +1807,9 @@
     VK_CULL_MODE_FRONT_AND_BACK                             = 0x00000003,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 type VkFlags VkSurfaceTransformFlagsKHR
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 bitfield VkSurfaceTransformFlagBitsKHR {
     VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR                       = 0x00000001,
     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR                      = 0x00000002,
@@ -1269,9 +1822,9 @@
     VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR                        = 0x00000100,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 type VkFlags VkCompositeAlphaFlagsKHR
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 bitfield VkCompositeAlphaFlagBitsKHR {
     VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR                       = 0x00000001,
     VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR               = 0x00000002,
@@ -1279,15 +1832,17 @@
     VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR                      = 0x00000008,
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 type VkFlags VkSwapchainCreateFlagsKHR
-//@extension("VK_KHR_swapchain")
-//bitfield VkSwapchainCreateFlagBitsKHR {
-//}
+@extension("VK_KHR_swapchain") // 2
+bitfield VkSwapchainCreateFlagBitsKHR {
+    //@extension("VK_KHX_device_group") // 61
+    VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX                    = 0x00000001,
+}
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplayPlaneAlphaFlagsKHR
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 bitfield VkDisplayPlaneAlphaFlagBitsKHR {
     VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR                   = 0x00000001,
     VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR                   = 0x00000002,
@@ -1295,57 +1850,64 @@
     VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR  = 0x00000008,
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplaySurfaceCreateFlagsKHR
-//@extension("VK_KHR_display")
+//@extension("VK_KHR_display") // 3
 //bitfield VkDisplaySurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplayModeCreateFlagsKHR
-//@extension("VK_KHR_display")
+//@extension("VK_KHR_display") // 3
 //bitfield VkDisplayModeCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 type VkFlags VkXlibSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xlib_surface")
+//@extension("VK_KHR_xlib_surface") // 5
 //bitfield VkXlibSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 type VkFlags VkXcbSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xcb_surface")
+//@extension("VK_KHR_xcb_surface") // 6
 //bitfield VkXcbSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 type VkFlags VkWaylandSurfaceCreateFlagsKHR
-//@extension("VK_KHR_wayland_surface")
+//@extension("VK_KHR_wayland_surface") // 7
 //bitfield VkWaylandSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 type VkFlags VkMirSurfaceCreateFlagsKHR
-//@extension("VK_KHR_mir_surface")
+//@extension("VK_KHR_mir_surface") // 8
 //bitfield VkMirSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 type VkFlags VkAndroidSurfaceCreateFlagsKHR
-//@extension("VK_KHR_android_surface")
+//@extension("VK_KHR_android_surface") // 9
 //bitfield VkAndroidSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 type VkFlags VkWin32SurfaceCreateFlagsKHR
-//@extension("VK_KHR_win32_surface")
+//@extension("VK_KHR_win32_surface") // 10
 //bitfield VkWin32SurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_EXT_debug_report")
+@extension("VK_ANDROID_native_buffer") // 11
+type VkFlags VkSwapchainImageUsageFlagsANDROID
+@extension("VK_ANDROID_native_buffer") // 11
+bitfield VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_SHARED_BIT_ANDROID = 0x00000001,
+}
+
+@extension("VK_EXT_debug_report") // 12
 type VkFlags VkDebugReportFlagsEXT
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 bitfield VkDebugReportFlagBitsEXT {
     VK_DEBUG_REPORT_INFORMATION_BIT_EXT                     = 0x00000001,
     VK_DEBUG_REPORT_WARNING_BIT_EXT                         = 0x00000002,
@@ -1354,6 +1916,159 @@
     VK_DEBUG_REPORT_DEBUG_BIT_EXT                           = 0x00000010,
 }
 
+@extension("VK_NV_external_memory_capabilities") // 56
+type VkFlags VkExternalMemoryHandleTypeFlagsNV
+@extension("VK_NV_external_memory_capabilities") // 56
+bitfield VkExternalMemoryHandleTypeFlagBitsNV {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV      = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV  = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV       = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV   = 0x00000008,
+}
+
+@extension("VK_NV_external_memory_capabilities") // 56
+type VkFlags VkExternalMemoryFeatureFlagsNV
+@extension("VK_NV_external_memory_capabilities") // 56
+bitfield VkExternalMemoryFeatureFlagBitsNV {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV        = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV            = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV            = 0x00000004,
+}
+
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkPeerMemoryFeatureFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkPeerMemoryFeatureFlagBitsKHX {
+    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX                 = 0x00000001,
+    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX                 = 0x00000002,
+    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX              = 0x00000004,
+    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX              = 0x00000008,
+}
+
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkMemoryAllocateFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkMemoryAllocateFlagBitsKHX {
+    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX                  = 0x00000001,
+}
+
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkDeviceGroupPresentModeFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkDeviceGroupPresentModeFlagBitsKHX {
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX              = 0x00000001,
+    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX             = 0x00000002,
+    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX                = 0x00000004,
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008,
+}
+
+@extension("VK_NN_vi_surface") // 63
+type VkFlags VkViSurfaceCreateFlagsNN
+//@extension("VK_NN_vi_surface") // 63
+//bitfield VkViSurfaceCreateFlagBitsNN {
+//}
+
+@extension("VK_KHR_maintenance1") // 70
+type VkFlags VkCommandPoolTrimFlagsKHR
+//@extension("VK_KHR_maintenance1") // 70
+//bitfield VkCommandPoolTrimFlagBitsKHR {
+//}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+type VkFlags VkExternalMemoryHandleTypeFlagsKHX
+@extension("VK_KHX_external_memory_capabilities") // 72
+bitfield VkExternalMemoryHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX            = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX         = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX     = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX        = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX    = 0x00000010,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX           = 0x00000020,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX       = 0x00000040,
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+type VkFlags VkExternalMemoryFeatureFlagsKHX
+@extension("VK_KHX_external_memory_capabilities") // 72
+bitfield VkExternalMemoryFeatureFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX           = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX               = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX               = 0x00000004,
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+type VkFlags VkExternalSemaphoreHandleTypeFlagsKHX
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+bitfield VkExternalSemaphoreHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX         = 0x00000001
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX      = 0x00000002
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX  = 0x00000004
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX       = 0x00000008
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX          = 0x00000010
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+type VkFlags VkExternalSemaphoreFeatureFlagsKHX
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+bitfield VkExternalSemaphoreFeatureFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX            = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX            = 0x00000002,
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+type VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR
+//@extension("VK_KHR_descriptor_update_template") // 86
+//bitfield VkDescriptorUpdateTemplateCreateFlagBitsKHR {
+//}
+
+@extension("VK_NVX_device_generated_commands") // 87
+type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands") // 87
+bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX {
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX   = 0x00000001,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX      = 0x00000002,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX      = 0x00000004,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX     = 0x00000008,
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+type VkFlags VkObjectEntryUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands") // 87
+bitfield VkObjectEntryUsageFlagBitsNVX {
+    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX                  = 0x00000001,
+    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX                   = 0x00000002,
+}
+
+@extension("VK_EXT_display_surface_counter") // 91
+type VkFlags VkSurfaceCounterFlagsEXT
+@extension("VK_EXT_display_surface_counter") // 91
+bitfield VkSurfaceCounterFlagBitsEXT {
+    VK_SURFACE_COUNTER_VBLANK_EXT                           = 0x00000001,
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+type VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV
+//@extension("VK_NV_viewport_swizzle") // 99
+//bitfield VkPipelineViewportSwizzleStateCreateFlagBitsNV {
+//}
+
+@extension("VK_EXT_discard_rectangles") // 100
+type VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT
+//@extension("VK_EXT_discard_rectangles") // 100
+//bitfield VkPipelineDiscardRectangleStateCreateFlagBitsEXT {
+//}
+
+@extension("VK_MVK_ios_surface") // 123
+type VkFlags VkIOSSurfaceCreateFlagsMVK
+//@extension("VK_MVK_ios_surface") // 123
+//bitfield VkIOSSurfaceCreateFlagBitsMVK {
+//}
+
+@extension("VK_MVK_macos_surface") // 124
+type VkFlags VkMacOSSurfaceCreateFlagsMVK
+//@extension("VK_MVK_macos_surface") // 124
+//bitfield VkMacOSSurfaceCreateFlagBitsMVK {
+//}
 
 //////////////////
 //  Structures  //
@@ -2459,7 +3174,7 @@
     u32                                         z
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 class VkSurfaceCapabilitiesKHR {
     u32                                         minImageCount
     u32                                         maxImageCount
@@ -2473,13 +3188,13 @@
     VkImageUsageFlags                           supportedUsageFlags
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 class VkSurfaceFormatKHR {
     VkFormat                                    format
     VkColorSpaceKHR                             colorSpace
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 class VkSwapchainCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2501,7 +3216,7 @@
     VkSwapchainKHR                              oldSwapchain
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 class VkPresentInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2513,7 +3228,7 @@
     VkResult*                                   pResults
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPropertiesKHR {
     VkDisplayKHR                                display
     const char*                                 displayName
@@ -2524,19 +3239,19 @@
     VkBool32                                    persistentContent
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModeParametersKHR {
     VkExtent2D                                  visibleRegion
     u32                                         refreshRate
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModePropertiesKHR {
     VkDisplayModeKHR                            displayMode
     VkDisplayModeParametersKHR                  parameters
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModeCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2544,13 +3259,13 @@
     VkDisplayModeParametersKHR                  parameters
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPlanePropertiesKHR {
     VkDisplayKHR                                currentDisplay
     u32                                         currentStackIndex
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPlaneCapabilitiesKHR {
     VkDisplayPlaneAlphaFlagsKHR                 supportedAlpha
     VkOffset2D                                  minSrcPosition
@@ -2563,7 +3278,7 @@
     VkExtent2D                                  maxDstExtent
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplaySurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2577,7 +3292,7 @@
     VkExtent2D                                  imageExtent
 }
 
-@extension("VK_KHR_display_swapchain")
+@extension("VK_KHR_display_swapchain") // 4
 class VkDisplayPresentInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2586,7 +3301,7 @@
     VkBool32                                    persistent
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 class VkXlibSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2595,7 +3310,7 @@
     platform.Window                             window
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 class VkXcbSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2604,7 +3319,7 @@
     platform.xcb_window_t                       window
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 class VkWaylandSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2613,7 +3328,7 @@
     platform.wl_surface*                        surface
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 class VkMirSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2622,7 +3337,7 @@
     platform.MirSurface*                        mirSurface
 }
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 class VkAndroidSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2630,7 +3345,7 @@
     platform.ANativeWindow*                     window
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 class VkWin32SurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2639,17 +3354,38 @@
     platform.HWND                               hwnd
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
+@internal class Gralloc1Usage {
+    u64                                         consumer
+    u64                                         producer
+}
+
+@extension("VK_ANDROID_native_buffer") // 11
 class VkNativeBufferANDROID {
     VkStructureType                             sType
     const void*                                 pNext
     platform.buffer_handle_t                    handle
-    int                                         stride
-    int                                         format
-    int                                         usage
+    s32                                         stride
+    s32                                         format
+    s32                                         usage
+    Gralloc1Usage                               usage2
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_ANDROID_native_buffer") // 11
+class VkSwapchainImageCreateInfoANDROID {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainImageUsageFlagsANDROID           flags
+}
+
+@extension("VK_ANDROID_native_buffer") // 11
+class VkPhysicalDevicePresentationPropertiesANDROID {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    sharedImage
+}
+
+@extension("VK_EXT_debug_report") // 12
 class VkDebugReportCallbackCreateInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2658,14 +3394,14 @@
     void*                                       pUserData
 }
 
-@extension("VK_AMD_rasterization_order")
+@extension("VK_AMD_rasterization_order") // 19
 class VkPipelineRasterizationStateRasterizationOrderAMD {
     VkStructureType                             sType
     const void*                                 pNext
     VkRasterizationOrderAMD                     rasterizationOrder
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerObjectNameInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2674,7 +3410,7 @@
     const char*                                 pObjectName
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerObjectTagInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2685,7 +3421,7 @@
     const void*                                 pTag
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerMarkerInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2693,6 +3429,881 @@
     f32[4]                                      color
 }
 
+@extension("VK_NV_dedicated_allocation") // 27
+class VkDedicatedAllocationImageCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation") // 27
+class VkDedicatedAllocationBufferCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation") // 27
+class VkDedicatedAllocationMemoryAllocateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkImage                                     image
+    VkBuffer                                    buffer
+}
+
+@extension("VK_KHX_multiview") // 54
+class VkRenderPassMultiviewCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         subpassCount
+    const u32*                                  pViewMasks
+    u32                                         dependencyCount
+    const s32*                                  pViewOffsets
+    u32                                         correlationMaskCount
+    const u32*                                  pCorrelationMasks
+}
+
+@extension("VK_KHX_multiview") // 54
+class VkPhysicalDeviceMultiviewFeaturesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    multiview
+    VkBool32                                    multiviewGeometryShader
+    VkBool32                                    multiviewTessellationShader
+}
+
+@extension("VK_KHX_multiview") // 54
+class VkPhysicalDeviceMultiviewPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         maxMultiviewViewCount
+    u32                                         maxMultiviewInstanceIndex
+}
+
+@extension("VK_NV_external_memory_capabilities") // 56
+class VkExternalImageFormatPropertiesNV {
+    VkImageFormatProperties                     imageFormatProperties
+    VkExternalMemoryFeatureFlagsNV              externalMemoryFeatures
+    VkExternalMemoryHandleTypeFlagsNV           exportFromImportedHandleTypes
+    VkExternalMemoryHandleTypeFlagsNV           compatibleHandleTypes
+}
+
+@extension("VK_NV_external_memory") // 57
+class VkExternalMemoryImageCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleTypes
+}
+
+@extension("VK_NV_external_memory") // 57
+class VkExportMemoryAllocateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleTypes
+}
+
+@extension("VK_NV_external_memory_win32") // 58
+class VkImportMemoryWin32HandleInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_NV_external_memory_win32") // 58
+class VkExportMemoryWin32HandleInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    platform.DWORD                              dwAccess
+}
+
+@extension("VK_NV_win32_keyed_mutex") // 59
+class VkWin32KeyedMutexAcquireReleaseInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         acquireCount
+    const VkDeviceMemory*                       pAcquireSyncs
+    const u64*                                  pAcquireKeys
+    const u32*                                  pAcquireTimeoutMilliseconds
+    u32                                         releaseCount
+    const VkDeviceMemory*                       pReleaseSyncs
+    const u64*                                  pReleaseKeys
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkPhysicalDeviceFeatures2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceFeatures                    features
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkPhysicalDeviceProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceProperties                  properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkFormatProperties                          formatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkImageFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkImageFormatProperties                     imageFormatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkFormat                                    format
+    VkImageType                                 type
+    VkImageTiling                               tiling
+    VkImageUsageFlags                           usage
+    VkImageCreateFlags                          flags
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkQueueFamilyProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkQueueFamilyProperties                     queueFamilyProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceMemoryProperties            memoryProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkSparseImageFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkSparseImageFormatProperties               properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+class VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkFormat                                    format
+    VkImageType                                 type
+    VkSampleCountFlagBits                       samples
+    VkImageUsageFlags                           usage
+    VkImageTiling                               tiling
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkMemoryAllocateFlagsInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkMemoryAllocateFlagsKHX                    flags
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindBufferMemoryInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBuffer                                    buffer
+    VkDeviceMemory                              memory
+    VkDeviceSize                                memoryOffset
+    u32                                         deviceIndexCount
+    const u32*                                  pDeviceIndices
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindImageMemoryInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkImage                                     image
+    VkDeviceMemory                              memory
+    VkDeviceSize                                memoryOffset
+    u32                                         deviceIndexCount
+    const u32*                                  pDeviceIndices
+    u32                                         SFRRectCount
+    const VkRect2D*                             pSFRRects
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupRenderPassBeginInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         deviceMask
+    u32                                         deviceRenderAreaCount
+    const VkRect2D*                             pDeviceRenderAreas
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupCommandBufferBeginInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupSubmitInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         waitSemaphoreCount
+    const u32*                                  pWaitSemaphoreDeviceIndices
+    u32                                         commandBufferCount
+    const u32*                                  pCommandBufferDeviceMasks
+    u32                                         signalSemaphoreCount
+    const u32*                                  pSignalSemaphoreDeviceIndices
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupBindSparseInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         resourceDeviceIndex
+    u32                                         memoryDeviceIndex
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupPresentCapabilitiesKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32[VK_MAX_DEVICE_GROUP_SIZE_KHX]           presentMask
+    VkDeviceGroupPresentModeFlagsKHX            modes
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkImageSwapchainCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindImageMemorySwapchainInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+    u32                                         imageIndex
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkAcquireNextImageInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+    u64                                         timeout
+    VkSemaphore                                 semaphore
+    VkFence                                     fence
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupPresentInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const u32*                                  pDeviceMasks
+    VkDeviceGroupPresentModeFlagBitsKHX         mode
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupSwapchainCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDeviceGroupPresentModeFlagsKHX            modes
+}
+
+@extension("VK_EXT_validation_flags") // 62
+class VkValidationFlagsEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         disabledValidationCheckCount
+    VkValidationCheckEXT*                       pDisabledValidationChecks
+}
+
+@extension("VK_NN_vi_surface") // 63
+class VkViSurfaceCreateInfoNN {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkViSurfaceCreateFlagsNN                    flags
+    void*                                       window
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+class VkPhysicalDeviceGroupPropertiesKHX {
+    VkStructureType                                 sType
+    void*                                           pNext
+    u32                                             physicalDeviceCount
+    VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE_KHX]  physicalDevices
+    VkBool32                                        subsetAllocation
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+class VkDeviceGroupDeviceCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         physicalDeviceCount
+    const VkPhysicalDevice*                     pPhysicalDevices
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalMemoryPropertiesKHX {
+    VkExternalMemoryFeatureFlagsKHX             externalMemoryFeatures
+    VkExternalMemoryHandleTypeFlagsKHX          exportFromImportedHandleTypes
+    VkExternalMemoryHandleTypeFlagsKHX          compatibleHandleTypes
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceExternalImageFormatInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalImageFormatPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalMemoryPropertiesKHX               externalMemoryProperties
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceExternalBufferInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBufferCreateFlags                         flags
+    VkBufferUsageFlags                          usage
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalBufferPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalMemoryPropertiesKHX               externalMemoryProperties
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceIDPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u8[VK_UUID_SIZE]                            deviceUUID
+    u8[VK_UUID_SIZE]                            driverUUID
+    u8[VK_LUID_SIZE_KHX]                        deviceLUID
+    VkBool32                                    deviceLUIDValid
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExternalMemoryImageCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExternalMemoryBufferCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExportMemoryAllocateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkImportMemoryWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkExportMemoryWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    platform.DWORD                              dwAccess
+    platform.LPCWSTR                            name
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkMemoryWin32HandlePropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         memoryTypeBits
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+class VkImportMemoryFdInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+    int                                         fd
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+class VkMemoryFdPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         memoryTypeBits
+}
+
+@extension("VK_KHX_win32_keyed_mutex") // 76
+class VkWin32KeyedMutexAcquireReleaseInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         acquireCount
+    const VkDeviceMemory*                       pAcquireSyncs
+    const u64*                                  pAcquireKeys
+    const u32*                                  pAcquireTimeouts
+    u32                                         releaseCount
+    const VkDeviceMemory*                       pReleaseSyncs
+    const u64*                                  pReleaseKeys
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+class VkPhysicalDeviceExternalSemaphoreInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+class VkExternalSemaphorePropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalSemaphoreHandleTypeFlagsKHX       exportFromImportedHandleTypes
+    VkExternalSemaphoreHandleTypeFlagsKHX       compatibleHandleTypes
+    VkExternalSemaphoreFeatureFlagsKHX          externalSemaphoreFeatures
+}
+
+@extension("VK_KHX_external_semaphore") // 78
+class VkExportSemaphoreCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalSemaphoreHandleTypeFlagsKHX       handleTypes
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkImportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSemaphore                                 semaphore
+    VkExternalSemaphoreHandleTypeFlagsKHX       handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkExportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    platform.DWORD                              dwAccess
+    platform.LPCWSTR                            name
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkD3D12FenceSubmitInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         waitSemaphoreValuesCount
+    const u64*                                  pWaitSemaphoreValues
+    u32                                         signalSemaphoreValuesCount
+    const u64*                                  pSignalSemaphoreValues
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+class VkImportSemaphoreFdInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSemaphore                                 semaphore
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType
+    s32                                         fd
+}
+
+@extension("VK_KHR_push_descriptor") // 81
+class VkPhysicalDevicePushDescriptorPropertiesKHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         maxPushDescriptors
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkRectLayerKHR {
+    VkOffset2D                                  offset
+    VkExtent2D                                  extent
+    u32                                         layer
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkPresentRegionKHR {
+    u32                                         rectangleCount
+    const VkRectLayerKHR*                       pRectangles
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkPresentRegionsKHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const VkPresentRegionKHR*                   pRegions
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+class VkDescriptorUpdateTemplateEntryKHR {
+    u32                                         dstBinding
+    u32                                         dstArrayElement
+    u32                                         descriptorCount
+    VkDescriptorType                            descriptorType
+    platform.size_t                             offset
+    platform.size_t                             stride
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+class VkDescriptorUpdateTemplateCreateInfoKHR {
+    VkStructureType                              sType
+    void*                                        pNext
+    VkDescriptorUpdateTemplateCreateFlagsKHR     flags
+    u32                                          descriptorUpdateEntryCount
+    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries
+    VkDescriptorUpdateTemplateTypeKHR            templateType
+    VkDescriptorSetLayout                        descriptorSetLayout
+    VkPipelineBindPoint                          pipelineBindPoint
+    VkPipelineLayout                             pipelineLayout
+    u32                                          set
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkDeviceGeneratedCommandsFeaturesNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    computeBindingPointSupport
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkDeviceGeneratedCommandsLimitsNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         maxIndirectCommandsLayoutTokenCount
+    u32                                         maxObjectEntryCounts
+    u32                                         minSequenceCountBufferOffsetAlignment
+    u32                                         minSequenceIndexBufferOffsetAlignment
+    u32                                         minCommandsTokenBufferOffsetAlignment
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkIndirectCommandsTokenNVX {
+    VkIndirectCommandsTokenTypeNVX              tokenType
+    VkBuffer                                    buffer
+    VkDeviceSize                                offset
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkIndirectCommandsLayoutTokenNVX {
+    VkIndirectCommandsTokenTypeNVX              tokenType
+    u32                                         bindingUnit
+    u32                                         dynamicCount
+    u32                                         divisor
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkIndirectCommandsLayoutCreateInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkPipelineBindPoint                         pipelineBindPoint
+    VkIndirectCommandsLayoutUsageFlagsNVX       flags
+    u32                                         tokenCount
+    const VkIndirectCommandsLayoutTokenNVX*     pTokens
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkCmdProcessCommandsInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkObjectTableNVX                            objectTable
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
+    u32                                         indirectCommandsTokenCount
+    const VkIndirectCommandsTokenNVX*           pIndirectCommandsTokens
+    u32                                         maxSequencesCount
+    VkCommandBuffer                             targetCommandBuffer
+    VkBuffer                                    sequencesCountBuffer
+    VkDeviceSize                                sequencesCountOffset
+    VkBuffer                                    sequencesIndexBuffer
+    VkDeviceSize                                sequencesIndexOffset
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkCmdReserveSpaceForCommandsInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkObjectTableNVX                            objectTable
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
+    u32                                         maxSequencesCount
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTableCreateInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         objectCount
+    const VkObjectEntryTypeNVX*                 pObjectEntryTypes
+    const u32*                                  pObjectEntryCounts
+    const VkObjectEntryUsageFlagsNVX*           pObjectEntryUsageFlags
+    u32                                         maxUniformBuffersPerDescriptor
+    u32                                         maxStorageBuffersPerDescriptor
+    u32                                         maxStorageImagesPerDescriptor
+    u32                                         maxSampledImagesPerDescriptor
+    u32                                         maxPipelineLayouts
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTableEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTablePipelineEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipeline                                  pipeline
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTableDescriptorSetEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipelineLayout                            pipelineLayout
+    VkDescriptorSet                             descriptorSet
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTableVertexBufferEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkBuffer                                    buffer
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTableIndexBufferEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkBuffer                                    buffer
+    VkIndexType                                 indexType
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+class VkObjectTablePushConstantEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipelineLayout                            pipelineLayout
+    VkShaderStageFlags                          stageFlags
+}
+
+@extension("VK_NV_clip_space_w_scaling") // 88
+class VkViewportWScalingNV {
+    f32                                         xcoeff
+    f32                                         ycoeff
+}
+
+@extension("VK_NV_clip_space_w_scaling") // 88
+class VkPipelineViewportWScalingStateCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    viewportWScalingEnable
+    u32                                         viewportCount
+    const VkViewportWScalingNV*                 pViewportWScalings
+}
+
+@extension("VK_EXT_display_surface_counter") // 91
+class VkSurfaceCapabilities2EXT {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         minImageCount
+    u32                                         maxImageCount
+    VkExtent2D                                  currentExtent
+    VkExtent2D                                  minImageExtent
+    VkExtent2D                                  maxImageExtent
+    u32                                         maxImageArrayLayers
+    VkSurfaceTransformFlagsKHR                  supportedTransforms
+    VkSurfaceTransformFlagBitsKHR               currentTransform
+    VkCompositeAlphaFlagsKHR                    supportedCompositeAlpha
+    VkImageUsageFlags                           supportedUsageFlags
+    VkSurfaceCounterFlagsEXT                    supportedSurfaceCounters
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDisplayPowerInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDisplayPowerStateEXT                      powerState
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDeviceEventInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDeviceEventTypeEXT                        deviceEvent
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDisplayEventInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDisplayEventTypeEXT                       displayEvent
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkSwapchainCounterCreateInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSurfaceCounterFlagsEXT                    surfaceCounters
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkRefreshCycleDurationGOOGLE {
+    u64                                             refreshDuration
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPastPresentationTimingGOOGLE {
+    u32                                             presentID
+    u64                                             desiredPresentTime
+    u64                                             actualPresentTime
+    u64                                             earliestPresentTime
+    u64                                             presentMargin
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPresentTimeGOOGLE {
+    u32                                             presentID
+    u64                                             desiredPresentTime
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPresentTimesInfoGOOGLE {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             swapchainCount
+    const VkPresentTimeGOOGLE*                      pTimes
+}
+
+@extension("VK_NVX_multiview_per_view_attributes") // 98
+class VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    perViewPositionAllComponents
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+class VkViewportSwizzleNV {
+    VkViewportCoordinateSwizzleNV               x
+    VkViewportCoordinateSwizzleNV               y
+    VkViewportCoordinateSwizzleNV               z
+    VkViewportCoordinateSwizzleNV               w
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+class VkPipelineViewportSwizzleStateCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkPipelineViewportSwizzleStateCreateFlagsNV flags
+    u32                                         viewportCount
+    const VkViewportSwizzleNV*                  pViewportSwizzles
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+class VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         maxDiscardRectangles
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+class VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkPipelineDiscardRectangleStateCreateFlagsEXT   flags
+    VkDiscardRectangleModeEXT                       discardRectangleMode
+    u32                                             discardRectangleCount
+    const VkRect2D*                                 pDiscardRectangles
+}
+
+@extension("VK_EXT_hdr_metadata") // 106
+class VkXYColorEXT {
+    f32                                             x
+    f32                                             y
+}
+
+@extension("VK_EXT_hdr_metadata") // 106
+class VkHdrMetadataEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkXYColorEXT                                    displayPrimaryRed
+    VkXYColorEXT                                    displayPrimaryGreen
+    VkXYColorEXT                                    displayPrimaryBlue
+    VkXYColorEXT                                    whitePoint
+    f32                                             maxLuminance
+    f32                                             minLuminance
+    f32                                             maxContentLightLevel
+    f32                                             maxFrameAverageLightLevel
+}
+
+@extension("VK_KHR_shared_presentable_image") // 111
+class VkSharedPresentSurfaceCapabilitiesKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkImageUsageFlags                               sharedPresentSupportedUsageFlags
+}
+
+@extension("VK_KHR_get_surface_capabilities2") // 119
+class VkPhysicalDeviceSurfaceInfo2KHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkSurfaceKHR                                    surface
+}
+
+@extension("VK_KHR_get_surface_capabilities2") // 119
+class VkSurfaceCapabilities2KHR {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkSurfaceCapabilitiesKHR                        surfaceCapabilities
+}
+
+@extension("VK_KHR_get_surface_capabilities2") // 119
+class VkSurfaceFormat2KHR {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkSurfaceFormatKHR                              surfaceFormat
+}
+
+@extension("VK_MVK_ios_surface") // 123
+class VkIOSSurfaceCreateInfoMVK {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkIOSSurfaceCreateFlagsMVK                      flags
+    const void*                                     pView
+}
+
+@extension("VK_MVK_macos_surface") // 124
+class VkMacOSSurfaceCreateInfoMVK {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkMacOSSurfaceCreateFlagsMVK                    flags
+    const void*                                     pView
+}
 
 ////////////////
 //  Commands  //
@@ -4415,9 +6026,9 @@
 @threadSafety("app")
 cmd void vkCmdDispatch(
         VkCommandBuffer                             commandBuffer,
-        u32                                         x,
-        u32                                         y,
-        u32                                         z) {
+        u32                                         groupCountX,
+        u32                                         groupCountY,
+        u32                                         groupCountZ) {
     commandBufferObject := GetCommandBuffer(commandBuffer)
 
     commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT)
@@ -4570,7 +6181,7 @@
         VkBuffer                                    dstBuffer,
         VkDeviceSize                                dstOffset,
         VkDeviceSize                                dataSize,
-        const u32*                                  pData) {
+        const void*                                 pData) {
     commandBufferObject := GetCommandBuffer(commandBuffer)
     dstBufferObject := GetBuffer(dstBuffer)
     assert(commandBufferObject.device == dstBufferObject.device)
@@ -4889,7 +6500,7 @@
     }
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd void vkDestroySurfaceKHR(
         VkInstance                                  instance,
         VkSurfaceKHR                                surface,
@@ -4901,7 +6512,7 @@
     State.Surfaces[surface] = null
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
         VkPhysicalDevice                            physicalDevice,
         u32                                         queueFamilyIndex,
@@ -4912,7 +6523,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -4925,7 +6536,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -4945,7 +6556,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -4965,7 +6576,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkCreateSwapchainKHR(
         VkDevice                                 device,
         const VkSwapchainCreateInfoKHR*          pCreateInfo,
@@ -4981,7 +6592,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd void vkDestroySwapchainKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -4993,7 +6604,7 @@
     State.Swapchains[swapchain] = null
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkGetSwapchainImagesKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -5014,7 +6625,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkAcquireNextImageKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -5031,7 +6642,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkQueuePresentKHR(
         VkQueue                                  queue,
         const VkPresentInfoKHR*                  pPresentInfo) {
@@ -5043,7 +6654,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetPhysicalDeviceDisplayPropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         u32*                                    pPropertyCount,
@@ -5052,7 +6663,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetPhysicalDeviceDisplayPlanePropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         u32*                                    pPropertyCount,
@@ -5061,7 +6672,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayPlaneSupportedDisplaysKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     planeIndex,
@@ -5071,7 +6682,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayModePropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayKHR                            display,
@@ -5081,7 +6692,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkCreateDisplayModeKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayKHR                            display,
@@ -5092,7 +6703,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayPlaneCapabilitiesKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayModeKHR                        mode,
@@ -5102,7 +6713,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkCreateDisplayPlaneSurfaceKHR(
         VkInstance                              instance,
         const VkDisplaySurfaceCreateInfoKHR*    pCreateInfo,
@@ -5111,7 +6722,7 @@
     return ?
 }
 
-@extension("VK_KHR_display_swapchain")
+@extension("VK_KHR_display_swapchain") // 4
 cmd VkResult vkCreateSharedSwapchainsKHR(
         VkDevice                                device,
         u32                                     swapchainCount,
@@ -5121,7 +6732,7 @@
     return ?
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 cmd VkResult vkCreateXlibSurfaceKHR(
         VkInstance                              instance,
         const VkXlibSurfaceCreateInfoKHR*       pCreateInfo,
@@ -5131,7 +6742,7 @@
     return ?
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 cmd VkBool32 vkGetPhysicalDeviceXlibPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5141,7 +6752,7 @@
     return ?
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 cmd VkResult vkCreateXcbSurfaceKHR(
         VkInstance                              instance,
         const VkXcbSurfaceCreateInfoKHR*        pCreateInfo,
@@ -5151,7 +6762,7 @@
     return ?
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 cmd VkBool32 vkGetPhysicalDeviceXcbPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5161,7 +6772,7 @@
     return ?
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 cmd VkResult vkCreateWaylandSurfaceKHR(
         VkInstance                              instance,
         const VkWaylandSurfaceCreateInfoKHR*    pCreateInfo,
@@ -5171,7 +6782,7 @@
     return ?
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 cmd VkBool32 vkGetPhysicalDeviceWaylandPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5180,7 +6791,7 @@
     return ?
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 cmd VkResult vkCreateMirSurfaceKHR(
         VkInstance                              instance,
         const VkMirSurfaceCreateInfoKHR*        pCreateInfo,
@@ -5190,7 +6801,7 @@
     return ?
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 cmd VkBool32 vkGetPhysicalDeviceMirPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5199,7 +6810,7 @@
     return ?
 }
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 cmd VkResult vkCreateAndroidSurfaceKHR(
         VkInstance                              instance,
         const VkAndroidSurfaceCreateInfoKHR*    pCreateInfo,
@@ -5209,7 +6820,7 @@
     return ?
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 cmd VkResult vkCreateWin32SurfaceKHR(
         VkInstance                              instance,
         const VkWin32SurfaceCreateInfoKHR*      pCreateInfo,
@@ -5219,7 +6830,7 @@
     return ?
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex) {
@@ -5227,16 +6838,29 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
+@optional
 cmd VkResult vkGetSwapchainGrallocUsageANDROID(
         VkDevice                                device,
         VkFormat                                format,
         VkImageUsageFlags                       imageUsage,
-        int*                                    grallocUsage) {
+        s32*                                    grallocUsage) {
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
+@optional
+cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
+        VkDevice                                device,
+        VkFormat                                format,
+        VkImageUsageFlags                       imageUsage,
+        VkSwapchainImageUsageFlagsANDROID       swapchainImageUsage,
+        u64*                                    grallocConsumerUsage,
+        u64*                                    grallocProducerUsage) {
+    return ?
+}
+
+@extension("VK_ANDROID_native_buffer") // 11
 cmd VkResult vkAcquireImageANDROID(
         VkDevice                                device,
         VkImage                                 image,
@@ -5246,7 +6870,7 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 cmd VkResult vkQueueSignalReleaseImageANDROID(
         VkQueue                                 queue,
         u32                                     waitSemaphoreCount,
@@ -5256,9 +6880,9 @@
     return ?
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 @external type void* PFN_vkDebugReportCallbackEXT
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 @pfn cmd VkBool32 vkDebugReportCallbackEXT(
         VkDebugReportFlagsEXT                   flags,
         VkDebugReportObjectTypeEXT              objectType,
@@ -5271,7 +6895,7 @@
     return ?
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd VkResult vkCreateDebugReportCallbackEXT(
         VkInstance                                  instance,
         const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
@@ -5280,14 +6904,14 @@
     return ?
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd void vkDestroyDebugReportCallbackEXT(
         VkInstance                                  instance,
         VkDebugReportCallbackEXT                    callback,
         const VkAllocationCallbacks*                pAllocator) {
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd void vkDebugReportMessageEXT(
         VkInstance                                  instance,
         VkDebugReportFlagsEXT                       flags,
@@ -5299,37 +6923,579 @@
         const char*                                 pMessage) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectTagEXT(
         VkDevice                                    device,
         VkDebugMarkerObjectTagInfoEXT*              pTagInfo) {
     return ?
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectNameEXT(
         VkDevice                                    device,
         VkDebugMarkerObjectNameInfoEXT*             pNameInfo) {
     return ?
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerBeginEXT(
         VkCommandBuffer                             commandBuffer,
         VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerEndEXT(
         VkCommandBuffer                             commandBuffer) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerInsertEXT(
         VkCommandBuffer                             commandBuffer,
         VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
 }
 
+@extension("VK_AMD_draw_indirect_count") // 34
+cmd void vkCmdDrawIndirectCountAMD(
+        VkCommandBuffer                             commandBuffer,
+        VkBuffer                                    buffer,
+        VkDeviceSize                                offset,
+        VkBuffer                                    countBuffer,
+        VkDeviceSize                                countBufferOffset,
+        u32                                         maxDrawCount,
+        u32                                         stride) {
+}
+
+@extension("VK_AMD_draw_indirect_count") // 34
+cmd void vkCmdDrawIndexedIndirectCountAMD(
+        VkCommandBuffer                             commandBuffer,
+        VkBuffer                                    buffer,
+        VkDeviceSize                                offset,
+        VkBuffer                                    countBuffer,
+        VkDeviceSize                                countBufferOffset,
+        u32                                         maxDrawCount,
+        u32                                         stride) {
+}
+
+@extension("VK_NV_external_memory_capabilities") // 56
+cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+        VkPhysicalDevice                            physicalDevice,
+        VkFormat                                    format,
+        VkImageType                                 type,
+        VkImageTiling                               tiling,
+        VkImageUsageFlags                           usage,
+        VkImageCreateFlags                          flags,
+        VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
+        VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32") // 58
+cmd VkResult vkGetMemoryWin32HandleNV(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagsNV           handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceFeatures2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceFeatures2KHR*               pFeatures) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceProperties2KHR*             pProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceFormatProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkFormat                                    format,
+        VkFormatProperties2KHR*                     pFormatProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
+        VkImageFormatProperties2KHR*                pImageFormatProperties) {
+    return ?
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        u32*                                        pQueueFamilyPropertyCount,
+        VkQueueFamilyProperties2KHR*                pQueueFamilyProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceMemoryProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2") // 60
+cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+        VkPhysicalDevice                                    physicalDevice,
+        const VkPhysicalDeviceSparseImageFormatInfo2KHR*    pFormatInfo,
+        u32*                                                pPropertyCount,
+        VkSparseImageFormatProperties2KHR*                  pProperties) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd void vkGetDeviceGroupPeerMemoryFeaturesKHX(
+        VkDevice                                    device,
+        u32                                         heapIndex,
+        u32                                         localDeviceIndex,
+        u32                                         remoteDeviceIndex,
+        VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkBindBufferMemory2KHX(
+        VkDevice                                    device,
+        u32                                         bindInfoCount,
+        const VkBindBufferMemoryInfoKHX*            pBindInfos) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkBindImageMemory2KHX(
+        VkDevice                                    device,
+        u32                                         bindInfoCount,
+        const VkBindImageMemoryInfoKHX*             pBindInfos) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd void vkCmdSetDeviceMaskKHX(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         deviceMask) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetDeviceGroupPresentCapabilitiesKHX(
+        VkDevice                                    device,
+        VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetDeviceGroupSurfacePresentModesKHX(
+        VkDevice                                    device,
+        VkSurfaceKHR                                surface,
+        VkDeviceGroupPresentModeFlagsKHX*           pModes) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkAcquireNextImage2KHX(
+        VkDevice                                    device,
+        const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+        u32*                                        pImageIndex) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd void vkCmdDispatchBaseKHX(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         baseGroupX,
+        u32                                         baseGroupY,
+        u32                                         baseGroupZ,
+        u32                                         groupCountX,
+        u32                                         groupCountY,
+        u32                                         groupCountZ) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetPhysicalDevicePresentRectanglesKHX(
+        VkPhysicalDevice                            physicalDevice,
+        VkSurfaceKHR                                surface,
+        u32*                                        pRectCount,
+        VkRect2D*                                   pRects) {
+    return ?
+}
+
+@extension("VK_NN_vi_surface") // 63
+cmd VkResult vkCreateViSurfaceNN(
+        VkInstance                                  instance,
+        const VkViSurfaceCreateInfoNN*              pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
+
+@extension("VK_KHR_maintenance1") // 70
+cmd void vkTrimCommandPoolKHR(
+        VkDevice                                    device,
+        VkCommandPool                               commandPool,
+        VkCommandPoolTrimFlagsKHR                   flags) {
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+cmd VkResult vkEnumeratePhysicalDeviceGroupsKHX(
+        VkInstance                                  instance,
+        u32*                                        pPhysicalDeviceGroupCount,
+        VkPhysicalDeviceGroupPropertiesKHX*         pPhysicalDeviceGroupProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+cmd void vkGetPhysicalDeviceExternalBufferPropertiesKHX(
+        VkPhysicalDevice                                physicalDevice,
+        const VkPhysicalDeviceExternalBufferInfoKHX*    pExternalBufferInfo,
+        VkExternalBufferPropertiesKHX*                  pExternalBufferProperties) {
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+cmd VkResult vkGetMemoryWin32HandleKHX(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+cmd VkResult vkGetMemoryWin32HandlePropertiesKHX(
+        VkDevice                                    device,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        platform.HANDLE                             handle,
+        VkMemoryWin32HandlePropertiesKHX*           pMemoryWin32HandleProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+cmd VkResult vkGetMemoryFdKHX(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        s32*                                        pFd) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+cmd VkResult vkGetMemoryFdPropertiesKHX(
+        VkDevice                                    device,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        s32                                         fd,
+        VkMemoryFdPropertiesKHX*                    pMemoryFdProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+cmd void vkGetPhysicalDeviceExternalSemaphorePropertiesKHX(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
+        VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties) {
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+cmd VkResult vkImportSemaphoreWin32HandleKHX(
+        VkDevice                                    device,
+        const VkImportSemaphoreWin32HandleInfoKHX*  pImportSemaphoreWin32HandleInfo) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+cmd VkResult vkGetSemaphoreWin32HandleKHX(
+        VkDevice                                    device,
+        VkSemaphore                                 semaphore,
+        VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+cmd VkResult vkImportSemaphoreFdKHX(
+        VkDevice                                    device,
+        const VkImportSemaphoreFdInfoKHX*           pImportSemaphoreFdInfo) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+cmd VkResult vkGetSemaphoreFdKHX(
+        VkDevice                                    device,
+        VkSemaphore                                 semaphore,
+        VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+        s32*                                        pFd) {
+    return ?
+}
+
+@extension("VK_KHR_push_descriptor") // 81
+cmd void vkCmdPushDescriptorSetKHR(
+        VkCommandBuffer                             commandBuffer,
+        VkPipelineBindPoint                         pipelineBindPoint,
+        VkPipelineLayout                            layout,
+        u32                                         set,
+        u32                                         descriptorWriteCount,
+        const VkWriteDescriptorSet*                 pDescriptorWrites) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd VkResult vkCreateDescriptorUpdateTemplateKHR(
+        VkDevice                                    device,
+        const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate) {
+    return ?
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkDestroyDescriptorUpdateTemplateKHR(
+        VkDevice                                    device,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkUpdateDescriptorSetWithTemplateKHR(
+        VkDevice                                    device,
+        VkDescriptorSet                             descriptorSet,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        const void*                                 pData) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkCmdPushDescriptorSetWithTemplateKHR(
+        VkCommandBuffer                             commandBuffer,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        VkPipelineLayout                            layout,
+        u32                                         set,
+        const void*                                 pData) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd void vkCmdProcessCommandsNVX(
+        VkCommandBuffer                             commandBuffer,
+        const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd void vkCmdReserveSpaceForCommandsNVX(
+        VkCommandBuffer                             commandBuffer,
+        const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd VkResult vkCreateIndirectCommandsLayoutNVX(
+        VkDevice                                    device,
+        const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout) {
+    return ?
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd void vkDestroyIndirectCommandsLayoutNVX(
+        VkDevice                                    device,
+        VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd VkResult vkCreateObjectTableNVX(
+        VkDevice                                    device,
+        const VkObjectTableCreateInfoNVX*           pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkObjectTableNVX*                           pObjectTable) {
+    return ?
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd void vkDestroyObjectTableNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd VkResult vkRegisterObjectsNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        u32                                         objectCount,
+        const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
+        const u32*                                  pObjectIndices) {
+    return ?
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd VkResult vkUnregisterObjectsNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        u32                                         objectCount,
+        const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
+        const u32*                                  pObjectIndices) {
+    return ?
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
+cmd void vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+        VkPhysicalDevice                            physicalDevice,
+        VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
+        VkDeviceGeneratedCommandsLimitsNVX*         pLimits) {
+}
+
+@extension("VK_NV_clip_space_w_scaling") // 88
+cmd void vkCmdSetViewportWScalingNV(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         firstViewport,
+        u32                                         viewportCount,
+        const VkViewportWScalingNV*                 pViewportWScalings) {
+}
+
+@extension("VK_EXT_direct_mode_display") // 89
+cmd VkResult vkReleaseDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        VkDisplayKHR                                display) {
+    return ?
+}
+
+@extension("VK_EXT_acquire_xlib_display") // 90
+cmd VkResult vkAcquireXlibDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        platform.Display*                           dpy,
+        VkDisplayKHR                                display) {
+    return ?
+}
+
+@extension("VK_EXT_acquire_xlib_display") // 90
+cmd VkResult vkGetRandROutputDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        platform.Display*                           dpy,
+        platform.RROutput                           rrOutput,
+        VkDisplayKHR*                               pDisplay) {
+    return ?
+}
+
+@extension("VK_EXT_display_surface_counter") // 91
+cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2EXT(
+        VkPhysicalDevice                            physicalDevice,
+        VkSurfaceKHR                                surface,
+        VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkDisplayPowerControlEXT(
+        VkDevice                                    device,
+        VkDisplayKHR                                display,
+        const VkDisplayPowerInfoEXT*                pDisplayPowerInfo) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkRegisterDeviceEventEXT(
+        VkDevice                                    device,
+        const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkFence*                                    pFence) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkRegisterDisplayEventEXT(
+        VkDevice                                    device,
+        VkDisplayKHR                                display,
+        const VkDisplayEventInfoEXT*                pDisplayEventInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkFence*                                    pFence) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkGetSwapchainCounterEXT(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        VkSurfaceCounterFlagBitsEXT                 counter,
+        u64*                                        pCounterValue) {
+    return ?
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+cmd VkResult vkGetRefreshCycleDurationGOOGLE(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties) {
+    deviceObject := GetDevice(device)
+    swapchainObject := GetSwapchain(swapchain)
+
+    displayTimingProperties := ?
+    pDisplayTimingProperties[0] = displayTimingProperties
+
+    return ?
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+cmd VkResult vkGetPastPresentationTimingGOOGLE(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        u32*                                        pPresentationTimingCount,
+        VkPastPresentationTimingGOOGLE*             pPresentationTimings) {
+    return ?
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+cmd void vkCmdSetDiscardRectangleEXT(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         firstDiscardRectangle,
+        u32                                         discardRectangleCount,
+        const VkRect2D*                             pDiscardRectangles) {
+}
+
+@extension("VK_EXT_hdr_metadata") // 106
+cmd void vkSetHdrMetadataEXT(
+        VkDevice                                    device,
+        u32                                         swapchainCount,
+        const VkSwapchainKHR*                       pSwapchains,
+        const VkHdrMetadataEXT*                     pMetadata) {
+}
+
+@extension("VK_KHR_shared_presentable_image") // 112
+cmd VkResult vkGetSwapchainStatusKHR(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain) {
+    return ?
+}
+
+@extension("VK_KHR_get_surface_capabilities2") // 119
+cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
+        VkSurfaceCapabilities2KHR*                  pSurfaceCapabilities) {
+    return ?
+}
+
+@extension("VK_KHR_get_surface_capabilities2") // 119
+cmd VkResult vkGetPhysicalDeviceSurfaceFormats2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
+        u32*                                        pSurfaceFormatCount,
+        VkSurfaceFormat2KHR*                        pSurfaceFormats) {
+    return ?
+}
+
+@extension("VK_MVK_ios_surface") // 123
+cmd VkResult vkCreateIOSSurfaceMVK(
+        VkInstance                                  instance,
+        const VkIOSSurfaceCreateInfoMVK*            pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
+
+@extension("VK_MVK_macos_surface") // 124
+cmd VkResult vkCreateMacOSSurfaceMVK(
+        VkInstance                                  instance,
+        const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
 
 ////////////////
 // Validation //
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index 7ace777..24af950 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -47,9 +47,33 @@
 
 The +vk_wsi_swapchin+ and +vk_wsi_device_swapchain+ extensions are primarily be implemented by the platform and live in +libvulkan.so+. The +VkSwapchain+ object and all interaction with +ANativeWindow+ will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver's +vkGetDeviceProcAddr+ functions, after passing through any enabled layers.
 
+Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on +format+ and +imageUsage+, but also on the intended usage of the swapchain. The swapchain usage bits are defined as
+[source,c]
+----
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+----
+
 Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling
 [source,c]
 ----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t*           grallocConsumerUsage,
+    uint64_t*           grallocProducerUsage,
+);
+----
+The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocConsumerUsage+ and +*grallocProducerUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
+
+An older version of this function is deprecated but still supported for backwards compatibility; it will be used if +vkGetSwapchainGrallocUsage2ANDROID+ is not supported:
+[source,c]
+----
 VkResult VKAPI vkGetSwapchainGrallocUsageANDROID(
     VkDevice            device,
     VkFormat            format,
@@ -57,7 +81,6 @@
     int*                grallocUsage
 );
 ----
-The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
 
 +VkNativeBufferANDROID+ is a +vkCreateImage+ extension structure for creating an image backed by a gralloc buffer. This structure is provided to +vkCreateImage+ in the +VkImageCreateInfo+ structure chain. Calls to +vkCreateImage+ with this structure will happen during the first call to +vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)+. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a +VkImage+ for each one.
 
@@ -73,11 +96,16 @@
 
     // Gralloc format and usage requested when the buffer was allocated.
     int                         format;
-    int                         usage;
+    int                         usage; // deprecated
+    struct {
+        uint64_t                consumer;
+        uint64_t                producer;
+    } usage2;
 } VkNativeBufferANDROID;
 ----
 
 When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
+[source,txt]
 ----
   .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
@@ -93,6 +121,17 @@
   .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
 ----
 
+Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a +VkSwapchainImageCreateInfoANDROID+ extension structure in the +VkImageCreateInfo+ chain provided to +vkCreateImage+, containing the swapchain image usage flags:
+[source,c]
+----
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagsANDROID      usage;
+} VkSwapchainImageCreateInfoANDROID;
+----
+
 +vkAcquireImageANDROID+ acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:
@@ -139,6 +178,8 @@
 
 This will be called during +vkQueuePresentWSI+ on the provided queue. Effects are similar to +vkQueueSignalSemaphore+, except with a native fence instead of a semaphore. The native fence must: not signal until the +waitSemaphoreCount+ semaphores in +pWaitSemaphores+ have signaled. Unlike +vkQueueSignalSemaphore+, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set +*pNativeFenceFd+ to -1. The file descriptor returned in +*pNativeFenceFd+ is owned and will be closed by the caller. Many drivers will be able to ignore the +image+ parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to +VK_IMAGE_LAYOUT_PRESENT_SRC_KHR+.
 
+If +image+ was created with +VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID+, then the driver must tolerate +vkQueueSignalReleaseImageANDROID+ being called repeatedly without intervening calls to +vkAcquireImageANDROID+.
+
 == History ==
 
 . *2015-07-08* Initial version
@@ -158,4 +199,11 @@
 . *2016-01-08*
    * Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
 . *2016-06-17*
-   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
+   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
+. *2017-01-06*
+   * Extension version 6
+   * Added VkSwapchainImageUsageFlagBitsANDROID
+   * Added vkGetSwapchainGrallocUsage2ANDROID
+   * Added VkSwapchainImageCreateInfoANDROID
+. *2017-02-09*
+   * Extended vkGetSwapchainGrallocUsage2ANDROID and VkNativeBufferANDROID to use gralloc1-style usage bitfields.
\ No newline at end of file
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..9fecce5 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -793,9 +793,35 @@
 <h2 id="_window_system_integration">2. Window System Integration</h2>
 <div class="sectionbody">
 <div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver&#8217;s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
+<div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The swapchain usage bits are defined as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
+<span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
+    <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
+    <span style="color: #008080">VkFormat</span>            format<span style="color: #990000">,</span>
+    <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
+    <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
+    uint64_t<span style="color: #990000">*</span>           grallocConsumerUsage<span style="color: #990000">,</span>
+    uint64_t<span style="color: #990000">*</span>           grallocProducerUsage<span style="color: #990000">,</span>
+<span style="color: #990000">);</span></tt></pre></div></div>
+<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocConsumerUsage</span> and <span class="monospaced">*grallocProducerUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
+<div class="paragraph"><p>An older version of this function is deprecated but still supported for backwards compatibility; it will be used if <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> is not supported:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -805,10 +831,9 @@
     <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                grallocUsage
 <span style="color: #990000">);</span></tt></pre></div></div>
-<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
 <div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -822,12 +847,19 @@
 
     <span style="font-style: italic"><span style="color: #9A1900">// Gralloc format and usage requested when the buffer was allocated.</span></span>
     <span style="color: #009900">int</span>                         format<span style="color: #990000">;</span>
-    <span style="color: #009900">int</span>                         usage<span style="color: #990000">;</span>
+    <span style="color: #009900">int</span>                         usage<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// deprecated</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+        <span style="color: #008080">uint64_t</span>                consumer<span style="color: #990000">;</span>
+        <span style="color: #008080">uint64_t</span>                producer<span style="color: #990000">;</span>
+    <span style="color: #FF0000">}</span> usage2<span style="color: #990000">;</span>
 <span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
 <div class="listingblock">
-<div class="content monospaced">
-<pre>  .imageType           = VK_IMAGE_TYPE_2D
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>  .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
   .extent              = the 2D dimensions requested for the gralloc buffer
   .mipLevels           = 1
@@ -838,13 +870,24 @@
   .flags               = 0
   .sharingMode         = VkSwapChainCreateInfoWSI::sharingMode
   .queueFamilyCount    = VkSwapChainCreateInfoWSI::queueFamilyCount
-  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</pre>
-</div></div>
+  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
+<div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+    <span style="color: #008080">VkStructureType</span>                        sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span>                            pNext<span style="color: #990000">;</span>
+
+    <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span>      usage<span style="color: #990000">;</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -872,7 +915,7 @@
 is as if the native fence was already signalled.</p></div>
 <div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -884,6 +927,7 @@
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                pNativeFenceFd
 <span style="color: #990000">);</span></tt></pre></div></div>
 <div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
+<div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
 </div>
 </div>
 <div class="sect1">
@@ -978,6 +1022,45 @@
 </li>
 </ul></div>
 </li>
+<li>
+<p>
+<strong>2017-01-06</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extension version 6
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageUsageFlagBitsANDROID
+</p>
+</li>
+<li>
+<p>
+Added vkGetSwapchainGrallocUsage2ANDROID
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageCreateInfoANDROID
+</p>
+</li>
+</ul></div>
+</li>
+<li>
+<p>
+<strong>2017-02-09</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extended vkGetSwapchainGrallocUsage2ANDROID and VkNativeBufferANDROID to use gralloc1-style usage bitfields.
+</p>
+</li>
+</ul></div>
+</li>
 </ol></div>
 </div>
 </div>
@@ -986,7 +1069,7 @@
 <div id="footer">
 <div id="footer-text">
 Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-02-09 22:40:30 PST
 </div>
 </div>
 </body>
diff --git a/vulkan/include/vulkan/NOTICE b/vulkan/include/vulkan/NOTICE
new file mode 100644
index 0000000..c958fba
--- /dev/null
+++ b/vulkan/include/vulkan/NOTICE
@@ -0,0 +1,13 @@
+Copyright (c) 2015-2016 The Khronos Group Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index d0ebf81..43a9a9c 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,11 +27,29 @@
 #define VK_ANDROID_native_buffer 1
 
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
+ *
+ * This version of the extension transitions from gralloc0 to gralloc1 usage
+ * flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
+ * to fill out deprecated fields in VkNativeBufferANDROID, and will call the
+ * deprecated vkGetSwapchainGrallocUsageANDROID if the new
+ * vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
+ * backwards-compatibility support is temporary, and will likely be removed in
+ * (along with all gralloc0 support) in a future release.
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     7
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME   "VK_ANDROID_native_buffer"
 
 #define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id)    ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
 #define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID   VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
 
 typedef struct {
     VkStructureType             sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
@@ -43,20 +61,53 @@
 
     // Gralloc format and usage requested when the buffer was allocated.
     int                         format;
-    int                         usage;
+    int                         usage; // DEPRECATED in SPEC_VERSION 6
+    // -- Added in SPEC_VERSION 6 --
+    struct {
+        uint64_t                consumer;
+        uint64_t                producer;
+    } usage2;
 } VkNativeBufferANDROID;
 
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagsANDROID      usage;
+} VkSwapchainImageCreateInfoANDROID;
+
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
+    const void*                            pNext;
+
+    VkBool32                               sharedImage;
+} VkPhysicalDevicePresentationPropertiesANDROID;
+
+// -- DEPRECATED in SPEC_VERSION 6 --
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+// -- ADDED in SPEC_VERSION 6 --
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 
 #ifndef VK_NO_PROTOTYPES
+
+// -- DEPRECATED in SPEC_VERSION 6 --
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
     VkDevice            device,
     VkFormat            format,
     VkImageUsageFlags   imageUsage,
     int*                grallocUsage
 );
+// -- ADDED in SPEC_VERSION 6 --
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t*           grallocConsumerUsage,
+    uint64_t*           grallocProducerUsage
+);
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
     VkDevice            device,
     VkImage             image,
@@ -71,17 +122,6 @@
     VkImage             image,
     int*                pNativeFenceFd
 );
-// -- DEPRECATED --
-VKAPI_ATTR VkResult VKAPI_CALL vkImportNativeFenceANDROID(
-    VkDevice            device,
-    VkSemaphore         semaphore,
-    int                 nativeFenceFd
-);
-VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalNativeFenceANDROID(
-    VkQueue             queue,
-    int*                pNativeFenceFd
-);
-// ----------------
 #endif
 
 #ifdef __cplusplus
diff --git a/vulkan/include/vulkan/vk_platform.h b/vulkan/include/vulkan/vk_platform.h
index 5d0fc76..6dc5eb5 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -2,7 +2,7 @@
 // File: vk_platform.h
 //
 /*
-** Copyright (c) 2014-2015 The Khronos Group Inc.
+** Copyright (c) 2014-2017 The Khronos Group Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -51,13 +51,13 @@
     #define VKAPI_ATTR
     #define VKAPI_CALL __stdcall
     #define VKAPI_PTR  VKAPI_CALL
-#elif defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__)
-    // Android does not support Vulkan in native code using the "armeabi" ABI.
-    #error "Vulkan requires the 'armeabi-v7a' or 'armeabi-v7a-hard' ABI on 32-bit ARM CPUs"
-#elif defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
-    // On Android/ARMv7a, Vulkan functions use the armeabi-v7a-hard calling
-    // convention, even if the application's native code is compiled with the
-    // armeabi-v7a calling convention.
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
+    #error "Vulkan isn't supported for the 'armeabi' NDK ABI"
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
+    // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
+    // calling convention, i.e. float parameters are passed in registers. This
+    // is true even if the rest of the application passes floats on the stack,
+    // as it does by default when compiling for the armeabi-v7a NDK ABI.
     #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
     #define VKAPI_CALL
     #define VKAPI_PTR  VKAPI_ATTR
@@ -94,7 +94,7 @@
 // controls inclusion of the extension interfaces in vulkan.h.
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
-#include <android/native_window.h>
+struct ANativeWindow;
 #endif
 
 #ifdef VK_USE_PLATFORM_MIR_KHR
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 2f18076..67eba86 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -6,7 +6,7 @@
 #endif
 
 /*
-** Copyright (c) 2015-2016 The Khronos Group Inc.
+** Copyright (c) 2015-2017 The Khronos Group Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -43,22 +43,24 @@
 #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
 #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 // Version of this file
-#define VK_HEADER_VERSION 13
+#define VK_HEADER_VERSION 46
 
 
 #define VK_NULL_HANDLE 0
-        
+
 
 
 #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
 
 
-#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
 #else
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
 #endif
-        
+#endif
+
 
 
 typedef uint32_t VkFlags;
@@ -135,6 +137,7 @@
     VK_ERROR_INCOMPATIBLE_DRIVER = -9,
     VK_ERROR_TOO_MANY_OBJECTS = -10,
     VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
+    VK_ERROR_FRAGMENTED_POOL = -12,
     VK_ERROR_SURFACE_LOST_KHR = -1000000000,
     VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,
     VK_SUBOPTIMAL_KHR = 1000001003,
@@ -142,9 +145,11 @@
     VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
     VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
     VK_ERROR_INVALID_SHADER_NV = -1000012000,
-    VK_RESULT_BEGIN_RANGE = VK_ERROR_FORMAT_NOT_SUPPORTED,
+    VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000,
+    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX = -1000072003,
+    VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL,
     VK_RESULT_END_RANGE = VK_INCOMPLETE,
-    VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FORMAT_NOT_SUPPORTED + 1),
+    VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1),
     VK_RESULT_MAX_ENUM = 0x7FFFFFFF
 } VkResult;
 
@@ -214,6 +219,94 @@
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX = 1000053000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX = 1000053001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX = 1000053002,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007,
+    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
+    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX = 1000070000,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX = 1000070001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX = 1000071000,
+    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX = 1000071001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX = 1000071002,
+    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX = 1000071003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX = 1000071004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHX = 1000071005,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHX = 1000071006,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHX = 1000071007,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX = 1000072000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX = 1000072001,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX = 1000072002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073001,
+    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX = 1000073002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX = 1000074000,
+    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX = 1000074001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX = 1000075000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX = 1000076000,
+    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX = 1000076001,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX = 1000077000,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078000,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078001,
+    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX = 1000078002,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX = 1000079000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000,
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000,
+    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
+    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001,
+    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003,
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000,
+    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001,
+    VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000,
+    VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001,
+    VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002,
+    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000,
+    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
     VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
     VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -426,6 +519,14 @@
     VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
     VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
     VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
     VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
     VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
     VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
@@ -493,6 +594,7 @@
     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7,
     VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002,
+    VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000,
     VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED,
     VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED,
     VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1),
@@ -674,6 +776,8 @@
     VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6,
     VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7,
     VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8,
+    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000,
+    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
     VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT,
     VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE,
     VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1),
@@ -814,6 +918,8 @@
     VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800,
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000,
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000,
+    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000,
+    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000,
     VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkFormatFeatureFlagBits;
 typedef VkFlags VkFormatFeatureFlags;
@@ -837,6 +943,8 @@
     VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
     VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008,
     VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
+    VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
+    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020,
     VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkImageCreateFlagBits;
 typedef VkFlags VkImageCreateFlags;
@@ -874,6 +982,7 @@
 
 typedef enum VkMemoryHeapFlagBits {
     VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001,
+    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX = 0x00000002,
     VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkMemoryHeapFlagBits;
 typedef VkFlags VkMemoryHeapFlags;
@@ -898,6 +1007,7 @@
     VK_PIPELINE_STAGE_HOST_BIT = 0x00004000,
     VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000,
     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000,
+    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000,
     VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkPipelineStageFlagBits;
 typedef VkFlags VkPipelineStageFlags;
@@ -990,6 +1100,8 @@
     VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001,
     VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002,
     VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004,
+    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008,
+    VK_PIPELINE_CREATE_DISPATCH_BASE_KHX = 0x00000010,
     VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkPipelineCreateFlagBits;
 typedef VkFlags VkPipelineCreateFlags;
@@ -1036,6 +1148,11 @@
 typedef VkFlags VkPipelineLayoutCreateFlags;
 typedef VkFlags VkShaderStageFlags;
 typedef VkFlags VkSamplerCreateFlags;
+
+typedef enum VkDescriptorSetLayoutCreateFlagBits {
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001,
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkDescriptorSetLayoutCreateFlagBits;
 typedef VkFlags VkDescriptorSetLayoutCreateFlags;
 
 typedef enum VkDescriptorPoolCreateFlagBits {
@@ -1052,6 +1169,12 @@
     VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkAttachmentDescriptionFlagBits;
 typedef VkFlags VkAttachmentDescriptionFlags;
+
+typedef enum VkSubpassDescriptionFlagBits {
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001,
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
+    VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSubpassDescriptionFlagBits;
 typedef VkFlags VkSubpassDescriptionFlags;
 
 typedef enum VkAccessFlagBits {
@@ -1072,12 +1195,16 @@
     VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
     VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
     VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
+    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000,
+    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000,
     VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkAccessFlagBits;
 typedef VkFlags VkAccessFlags;
 
 typedef enum VkDependencyFlagBits {
     VK_DEPENDENCY_BY_REGION_BIT = 0x00000001,
+    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX = 0x00000002,
+    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX = 0x00000004,
     VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkDependencyFlagBits;
 typedef VkFlags VkDependencyFlags;
@@ -2340,14 +2467,14 @@
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -2976,9 +3103,9 @@
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDispatch(
     VkCommandBuffer                             commandBuffer,
-    uint32_t                                    x,
-    uint32_t                                    y,
-    uint32_t                                    z);
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect(
     VkCommandBuffer                             commandBuffer,
@@ -3032,7 +3159,7 @@
     VkBuffer                                    dstBuffer,
     VkDeviceSize                                dstOffset,
     VkDeviceSize                                dataSize,
-    const uint32_t*                             pData);
+    const void*                                 pData);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(
     VkCommandBuffer                             commandBuffer,
@@ -3177,6 +3304,19 @@
 
 typedef enum VkColorSpaceKHR {
     VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0,
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001,
+    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104003,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007,
+    VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008,
+    VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009,
+    VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012,
+    VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013,
     VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1),
@@ -3188,6 +3328,8 @@
     VK_PRESENT_MODE_MAILBOX_KHR = 1,
     VK_PRESENT_MODE_FIFO_KHR = 2,
     VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3,
+    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000,
+    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001,
     VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR,
     VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR,
     VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1),
@@ -3279,6 +3421,11 @@
 #define VK_KHR_SWAPCHAIN_SPEC_VERSION     68
 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME   "VK_KHR_swapchain"
 
+
+typedef enum VkSwapchainCreateFlagBitsKHR {
+    VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX = 0x00000001,
+    VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSwapchainCreateFlagBitsKHR;
 typedef VkFlags VkSwapchainCreateFlagsKHR;
 
 typedef struct VkSwapchainCreateInfoKHR {
@@ -3579,7 +3726,7 @@
 #define VK_KHR_wayland_surface 1
 #include <wayland-client.h>
 
-#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 5
+#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
 #define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface"
 
 typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
@@ -3647,7 +3794,6 @@
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
 #define VK_KHR_android_surface 1
-#include <android/native_window.h>
 
 #define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6
 #define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface"
@@ -3658,7 +3804,7 @@
     VkStructureType                   sType;
     const void*                       pNext;
     VkAndroidSurfaceCreateFlagsKHR    flags;
-    ANativeWindow*                    window;
+    struct ANativeWindow*             window;
 } VkAndroidSurfaceCreateInfoKHR;
 
 
@@ -3712,10 +3858,259 @@
 #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge"
 
 
+#define VK_KHR_get_physical_device_properties2 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+typedef struct VkPhysicalDeviceFeatures2KHR {
+    VkStructureType             sType;
+    void*                       pNext;
+    VkPhysicalDeviceFeatures    features;
+} VkPhysicalDeviceFeatures2KHR;
+
+typedef struct VkPhysicalDeviceProperties2KHR {
+    VkStructureType               sType;
+    void*                         pNext;
+    VkPhysicalDeviceProperties    properties;
+} VkPhysicalDeviceProperties2KHR;
+
+typedef struct VkFormatProperties2KHR {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkFormatProperties    formatProperties;
+} VkFormatProperties2KHR;
+
+typedef struct VkImageFormatProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkImageFormatProperties    imageFormatProperties;
+} VkImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkFormat              format;
+    VkImageType           type;
+    VkImageTiling         tiling;
+    VkImageUsageFlags     usage;
+    VkImageCreateFlags    flags;
+} VkPhysicalDeviceImageFormatInfo2KHR;
+
+typedef struct VkQueueFamilyProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkQueueFamilyProperties    queueFamilyProperties;
+} VkQueueFamilyProperties2KHR;
+
+typedef struct VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                     sType;
+    void*                               pNext;
+    VkPhysicalDeviceMemoryProperties    memoryProperties;
+} VkPhysicalDeviceMemoryProperties2KHR;
+
+typedef struct VkSparseImageFormatProperties2KHR {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkSparseImageFormatProperties    properties;
+} VkSparseImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkFormat                 format;
+    VkImageType              type;
+    VkSampleCountFlagBits    samples;
+    VkImageUsageFlags        usage;
+    VkImageTiling            tiling;
+} VkPhysicalDeviceSparseImageFormatInfo2KHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceFeatures2KHR*               pFeatures);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceProperties2KHR*             pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkFormatProperties2KHR*                     pFormatProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
+    VkImageFormatProperties2KHR*                pImageFormatProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties2KHR*                pQueueFamilyProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+    uint32_t*                                   pPropertyCount,
+    VkSparseImageFormatProperties2KHR*          pProperties);
+#endif
+
+#define VK_KHR_shader_draw_parameters 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
+
+
+#define VK_KHR_maintenance1 1
+#define VK_KHR_MAINTENANCE1_SPEC_VERSION  1
+#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
+
+typedef VkFlags VkCommandPoolTrimFlagsKHR;
+
+typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR(
+    VkDevice                                    device,
+    VkCommandPool                               commandPool,
+    VkCommandPoolTrimFlagsKHR                   flags);
+#endif
+
+#define VK_KHR_push_descriptor 1
+#define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1
+#define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
+
+typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxPushDescriptors;
+} VkPhysicalDevicePushDescriptorPropertiesKHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineBindPoint                         pipelineBindPoint,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    uint32_t                                    descriptorWriteCount,
+    const VkWriteDescriptorSet*                 pDescriptorWrites);
+#endif
+
+#define VK_KHR_incremental_present 1
+#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+typedef struct VkRectLayerKHR {
+    VkOffset2D    offset;
+    VkExtent2D    extent;
+    uint32_t      layer;
+} VkRectLayerKHR;
+
+typedef struct VkPresentRegionKHR {
+    uint32_t                 rectangleCount;
+    const VkRectLayerKHR*    pRectangles;
+} VkPresentRegionKHR;
+
+typedef struct VkPresentRegionsKHR {
+    VkStructureType              sType;
+    const void*                  pNext;
+    uint32_t                     swapchainCount;
+    const VkPresentRegionKHR*    pRegions;
+} VkPresentRegionsKHR;
+
+
+
+#define VK_KHR_descriptor_update_template 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplateKHR)
+
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
+
+
+typedef enum VkDescriptorUpdateTemplateTypeKHR {
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = 0,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE_KHR = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR + 1),
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkDescriptorUpdateTemplateTypeKHR;
+
+typedef VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR;
+
+typedef struct VkDescriptorUpdateTemplateEntryKHR {
+    uint32_t            dstBinding;
+    uint32_t            dstArrayElement;
+    uint32_t            descriptorCount;
+    VkDescriptorType    descriptorType;
+    size_t              offset;
+    size_t              stride;
+} VkDescriptorUpdateTemplateEntryKHR;
+
+typedef struct VkDescriptorUpdateTemplateCreateInfoKHR {
+    VkStructureType                              sType;
+    void*                                        pNext;
+    VkDescriptorUpdateTemplateCreateFlagsKHR     flags;
+    uint32_t                                     descriptorUpdateEntryCount;
+    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries;
+    VkDescriptorUpdateTemplateTypeKHR            templateType;
+    VkDescriptorSetLayout                        descriptorSetLayout;
+    VkPipelineBindPoint                          pipelineBindPoint;
+    VkPipelineLayout                             pipelineLayout;
+    uint32_t                                     set;
+} VkDescriptorUpdateTemplateCreateInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate);
+typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorSet                             descriptorSet,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const void*                                 pData);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    const void*                                 pData);
+#endif
+
 #define VK_EXT_debug_report 1
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
 
-#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  2
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  6
 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
 #define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT
 
@@ -3750,9 +4145,14 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
     VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
     VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
+    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
     VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
     VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
 } VkDebugReportObjectTypeEXT;
 
@@ -3855,8 +4255,18 @@
 
 
 
+#define VK_AMD_shader_trinary_minmax 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+
+#define VK_AMD_shader_explicit_vertex_parameter 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
+
 #define VK_EXT_debug_marker 1
-#define VK_EXT_DEBUG_MARKER_SPEC_VERSION  3
+#define VK_EXT_DEBUG_MARKER_SPEC_VERSION  4
 #define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker"
 
 typedef struct VkDebugMarkerObjectNameInfoEXT {
@@ -3912,6 +4322,1566 @@
     VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
 #endif
 
+#define VK_AMD_gcn_shader 1
+#define VK_AMD_GCN_SHADER_SPEC_VERSION    1
+#define VK_AMD_GCN_SHADER_EXTENSION_NAME  "VK_AMD_gcn_shader"
+
+
+#define VK_NV_dedicated_allocation 1
+#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+typedef struct VkDedicatedAllocationImageCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationImageCreateInfoNV;
+
+typedef struct VkDedicatedAllocationBufferCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationBufferCreateInfoNV;
+
+typedef struct VkDedicatedAllocationMemoryAllocateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkBuffer           buffer;
+} VkDedicatedAllocationMemoryAllocateInfoNV;
+
+
+
+#define VK_AMD_draw_indirect_count 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+#endif
+
+#define VK_AMD_negative_viewport_height 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+
+#define VK_AMD_gpu_shader_half_float 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+
+#define VK_AMD_shader_ballot 1
+#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+
+#define VK_KHX_multiview 1
+#define VK_KHX_MULTIVIEW_SPEC_VERSION     1
+#define VK_KHX_MULTIVIEW_EXTENSION_NAME   "VK_KHX_multiview"
+
+typedef struct VkRenderPassMultiviewCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           subpassCount;
+    const uint32_t*    pViewMasks;
+    uint32_t           dependencyCount;
+    const int32_t*     pViewOffsets;
+    uint32_t           correlationMaskCount;
+    const uint32_t*    pCorrelationMasks;
+} VkRenderPassMultiviewCreateInfoKHX;
+
+typedef struct VkPhysicalDeviceMultiviewFeaturesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           multiview;
+    VkBool32           multiviewGeometryShader;
+    VkBool32           multiviewTessellationShader;
+} VkPhysicalDeviceMultiviewFeaturesKHX;
+
+typedef struct VkPhysicalDeviceMultiviewPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxMultiviewViewCount;
+    uint32_t           maxMultiviewInstanceIndex;
+} VkPhysicalDeviceMultiviewPropertiesKHX;
+
+
+
+#define VK_IMG_format_pvrtc 1
+#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION  1
+#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+
+#define VK_NV_external_memory_capabilities 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsNV {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsNV;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsNV;
+
+typedef enum VkExternalMemoryFeatureFlagBitsNV {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsNV;
+typedef VkFlags VkExternalMemoryFeatureFlagsNV;
+
+typedef struct VkExternalImageFormatPropertiesNV {
+    VkImageFormatProperties              imageFormatProperties;
+    VkExternalMemoryFeatureFlagsNV       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsNV    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsNV    compatibleHandleTypes;
+} VkExternalImageFormatPropertiesNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkImageType                                 type,
+    VkImageTiling                               tiling,
+    VkImageUsageFlags                           usage,
+    VkImageCreateFlags                          flags,
+    VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
+    VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties);
+#endif
+
+#define VK_NV_external_memory 1
+#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+typedef struct VkExternalMemoryImageCreateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExternalMemoryImageCreateInfoNV;
+
+typedef struct VkExportMemoryAllocateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExportMemoryAllocateInfoNV;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_external_memory_win32 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+typedef struct VkImportMemoryWin32HandleInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleType;
+    HANDLE                               handle;
+} VkImportMemoryWin32HandleInfoNV;
+
+typedef struct VkExportMemoryWin32HandleInfoNV {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+} VkExportMemoryWin32HandleInfoNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagsNV           handleType,
+    HANDLE*                                     pHandle);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_win32_keyed_mutex 1
+#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 acquireCount;
+    const VkDeviceMemory*    pAcquireSyncs;
+    const uint64_t*          pAcquireKeys;
+    const uint32_t*          pAcquireTimeoutMilliseconds;
+    uint32_t                 releaseCount;
+    const VkDeviceMemory*    pReleaseSyncs;
+    const uint64_t*          pReleaseKeys;
+} VkWin32KeyedMutexAcquireReleaseInfoNV;
+
+
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_KHX_device_group 1
+#define VK_MAX_DEVICE_GROUP_SIZE_KHX      32
+#define VK_KHX_DEVICE_GROUP_SPEC_VERSION  1
+#define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+
+
+typedef enum VkPeerMemoryFeatureFlagBitsKHX {
+    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX = 0x00000001,
+    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX = 0x00000002,
+    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX = 0x00000004,
+    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX = 0x00000008,
+    VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkPeerMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkPeerMemoryFeatureFlagsKHX;
+
+typedef enum VkMemoryAllocateFlagBitsKHX {
+    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX = 0x00000001,
+    VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkMemoryAllocateFlagBitsKHX;
+typedef VkFlags VkMemoryAllocateFlagsKHX;
+
+typedef enum VkDeviceGroupPresentModeFlagBitsKHX {
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX = 0x00000001,
+    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX = 0x00000002,
+    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX = 0x00000004,
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008,
+    VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkDeviceGroupPresentModeFlagBitsKHX;
+typedef VkFlags VkDeviceGroupPresentModeFlagsKHX;
+
+typedef struct VkMemoryAllocateFlagsInfoKHX {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkMemoryAllocateFlagsKHX    flags;
+    uint32_t                    deviceMask;
+} VkMemoryAllocateFlagsInfoKHX;
+
+typedef struct VkBindBufferMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBuffer           buffer;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+} VkBindBufferMemoryInfoKHX;
+
+typedef struct VkBindImageMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+    uint32_t           SFRRectCount;
+    const VkRect2D*    pSFRRects;
+} VkBindImageMemoryInfoKHX;
+
+typedef struct VkDeviceGroupRenderPassBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+    uint32_t           deviceRenderAreaCount;
+    const VkRect2D*    pDeviceRenderAreas;
+} VkDeviceGroupRenderPassBeginInfoKHX;
+
+typedef struct VkDeviceGroupCommandBufferBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+} VkDeviceGroupCommandBufferBeginInfoKHX;
+
+typedef struct VkDeviceGroupSubmitInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           waitSemaphoreCount;
+    const uint32_t*    pWaitSemaphoreDeviceIndices;
+    uint32_t           commandBufferCount;
+    const uint32_t*    pCommandBufferDeviceMasks;
+    uint32_t           signalSemaphoreCount;
+    const uint32_t*    pSignalSemaphoreDeviceIndices;
+} VkDeviceGroupSubmitInfoKHX;
+
+typedef struct VkDeviceGroupBindSparseInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           resourceDeviceIndex;
+    uint32_t           memoryDeviceIndex;
+} VkDeviceGroupBindSparseInfoKHX;
+
+typedef struct VkDeviceGroupPresentCapabilitiesKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    uint32_t                            presentMask[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupPresentCapabilitiesKHX;
+
+typedef struct VkImageSwapchainCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+} VkImageSwapchainCreateInfoKHX;
+
+typedef struct VkBindImageMemorySwapchainInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint32_t           imageIndex;
+} VkBindImageMemorySwapchainInfoKHX;
+
+typedef struct VkAcquireNextImageInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint64_t           timeout;
+    VkSemaphore        semaphore;
+    VkFence            fence;
+    uint32_t           deviceMask;
+} VkAcquireNextImageInfoKHX;
+
+typedef struct VkDeviceGroupPresentInfoKHX {
+    VkStructureType                        sType;
+    const void*                            pNext;
+    uint32_t                               swapchainCount;
+    const uint32_t*                        pDeviceMasks;
+    VkDeviceGroupPresentModeFlagBitsKHX    mode;
+} VkDeviceGroupPresentInfoKHX;
+
+typedef struct VkDeviceGroupSwapchainCreateInfoKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupSwapchainCreateInfoKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos);
+typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX(
+    VkDevice                                    device,
+    uint32_t                                    heapIndex,
+    uint32_t                                    localDeviceIndex,
+    uint32_t                                    remoteDeviceIndex,
+    VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindBufferMemoryInfoKHX*            pBindInfos);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindImageMemoryInfoKHX*             pBindInfos);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    deviceMask);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
+    VkDevice                                    device,
+    VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
+    VkDevice                                    device,
+    VkSurfaceKHR                                surface,
+    VkDeviceGroupPresentModeFlagsKHX*           pModes);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
+    VkDevice                                    device,
+    const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+    uint32_t*                                   pImageIndex);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    baseGroupX,
+    uint32_t                                    baseGroupY,
+    uint32_t                                    baseGroupZ,
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    uint32_t*                                   pRectCount,
+    VkRect2D*                                   pRects);
+#endif
+
+#define VK_EXT_validation_flags 1
+#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+
+typedef enum VkValidationCheckEXT {
+    VK_VALIDATION_CHECK_ALL_EXT = 0,
+    VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1),
+    VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCheckEXT;
+
+typedef struct VkValidationFlagsEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 disabledValidationCheckCount;
+    VkValidationCheckEXT*    pDisabledValidationChecks;
+} VkValidationFlagsEXT;
+
+
+
+#ifdef VK_USE_PLATFORM_VI_NN
+#define VK_NN_vi_surface 1
+#define VK_NN_VI_SURFACE_SPEC_VERSION     1
+#define VK_NN_VI_SURFACE_EXTENSION_NAME   "VK_NN_vi_surface"
+
+typedef VkFlags VkViSurfaceCreateFlagsNN;
+
+typedef struct VkViSurfaceCreateInfoNN {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkViSurfaceCreateFlagsNN    flags;
+    void*                       window;
+} VkViSurfaceCreateInfoNN;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN(
+    VkInstance                                  instance,
+    const VkViSurfaceCreateInfoNN*              pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_VI_NN */
+
+#define VK_EXT_shader_subgroup_ballot 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
+
+
+#define VK_EXT_shader_subgroup_vote 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
+
+
+#define VK_KHX_device_group_creation 1
+#define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1
+#define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation"
+
+typedef struct VkPhysicalDeviceGroupPropertiesKHX {
+    VkStructureType     sType;
+    void*               pNext;
+    uint32_t            physicalDeviceCount;
+    VkPhysicalDevice    physicalDevices[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkBool32            subsetAllocation;
+} VkPhysicalDeviceGroupPropertiesKHX;
+
+typedef struct VkDeviceGroupDeviceCreateInfoKHX {
+    VkStructureType            sType;
+    const void*                pNext;
+    uint32_t                   physicalDeviceCount;
+    const VkPhysicalDevice*    pPhysicalDevices;
+} VkDeviceGroupDeviceCreateInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHX)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHX(
+    VkInstance                                  instance,
+    uint32_t*                                   pPhysicalDeviceGroupCount,
+    VkPhysicalDeviceGroupPropertiesKHX*         pPhysicalDeviceGroupProperties);
+#endif
+
+#define VK_KHX_external_memory_capabilities 1
+#define VK_LUID_SIZE_KHX                  8
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX = 0x00000020,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX = 0x00000040,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsKHX;
+
+typedef enum VkExternalMemoryFeatureFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkExternalMemoryFeatureFlagsKHX;
+
+typedef struct VkExternalMemoryPropertiesKHX {
+    VkExternalMemoryFeatureFlagsKHX       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsKHX    compatibleHandleTypes;
+} VkExternalMemoryPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalImageFormatInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalImageFormatInfoKHX;
+
+typedef struct VkExternalImageFormatPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalImageFormatPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalBufferInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkBufferCreateFlags                      flags;
+    VkBufferUsageFlags                       usage;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalBufferInfoKHX;
+
+typedef struct VkExternalBufferPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalBufferPropertiesKHX;
+
+typedef struct VkPhysicalDeviceIDPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint8_t            deviceUUID[VK_UUID_SIZE];
+    uint8_t            driverUUID[VK_UUID_SIZE];
+    uint8_t            deviceLUID[VK_LUID_SIZE_KHX];
+    VkBool32           deviceLUIDValid;
+} VkPhysicalDeviceIDPropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, VkExternalBufferPropertiesKHX* pExternalBufferProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo,
+    VkExternalBufferPropertiesKHX*              pExternalBufferProperties);
+#endif
+
+#define VK_KHX_external_memory 1
+#define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory"
+#define VK_QUEUE_FAMILY_EXTERNAL_KHX      (~0U-1)
+
+typedef struct VkExternalMemoryImageCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryImageCreateInfoKHX;
+
+typedef struct VkExternalMemoryBufferCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryBufferCreateInfoKHX;
+
+typedef struct VkExportMemoryAllocateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExportMemoryAllocateInfoKHX;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHX
+#define VK_KHX_external_memory_win32 1
+#define VK_KHX_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHX_external_memory_win32"
+
+typedef struct VkImportMemoryWin32HandleInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+    HANDLE                                   handle;
+} VkImportMemoryWin32HandleInfoKHX;
+
+typedef struct VkExportMemoryWin32HandleInfoKHX {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+    LPCWSTR                       name;
+} VkExportMemoryWin32HandleInfoKHX;
+
+typedef struct VkMemoryWin32HandlePropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           memoryTypeBits;
+} VkMemoryWin32HandlePropertiesKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle);
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHX(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    HANDLE*                                     pHandle);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHX(
+    VkDevice                                    device,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    HANDLE                                      handle,
+    VkMemoryWin32HandlePropertiesKHX*           pMemoryWin32HandleProperties);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHX */
+
+#define VK_KHX_external_memory_fd 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd"
+
+typedef struct VkImportMemoryFdInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+    int                                      fd;
+} VkImportMemoryFdInfoKHX;
+
+typedef struct VkMemoryFdPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           memoryTypeBits;
+} VkMemoryFdPropertiesKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd);
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, VkMemoryFdPropertiesKHX* pMemoryFdProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHX(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int*                                        pFd);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHX(
+    VkDevice                                    device,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int                                         fd,
+    VkMemoryFdPropertiesKHX*                    pMemoryFdProperties);
+#endif
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_KHX_win32_keyed_mutex 1
+#define VK_KHX_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+#define VK_KHX_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHX_win32_keyed_mutex"
+
+typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHX {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 acquireCount;
+    const VkDeviceMemory*    pAcquireSyncs;
+    const uint64_t*          pAcquireKeys;
+    const uint32_t*          pAcquireTimeouts;
+    uint32_t                 releaseCount;
+    const VkDeviceMemory*    pReleaseSyncs;
+    const uint64_t*          pReleaseKeys;
+} VkWin32KeyedMutexAcquireReleaseInfoKHX;
+
+
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_KHX_external_semaphore_capabilities 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities"
+
+
+typedef enum VkExternalSemaphoreHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreHandleTypeFlagsKHX;
+
+typedef enum VkExternalSemaphoreFeatureFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreFeatureFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreFeatureFlagsKHX;
+
+typedef struct VkPhysicalDeviceExternalSemaphoreInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalSemaphoreInfoKHX;
+
+typedef struct VkExternalSemaphorePropertiesKHX {
+    VkStructureType                          sType;
+    void*                                    pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalSemaphoreHandleTypeFlagsKHX    compatibleHandleTypes;
+    VkExternalSemaphoreFeatureFlagsKHX       externalSemaphoreFeatures;
+} VkExternalSemaphorePropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
+    VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties);
+#endif
+
+#define VK_KHX_external_semaphore 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore"
+
+typedef struct VkExportSemaphoreCreateInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    handleTypes;
+} VkExportSemaphoreCreateInfoKHX;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHX
+#define VK_KHX_external_semaphore_win32 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHX_external_semaphore_win32"
+
+typedef struct VkImportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkSemaphore                              semaphore;
+    VkExternalSemaphoreHandleTypeFlagsKHX    handleType;
+    HANDLE                                   handle;
+} VkImportSemaphoreWin32HandleInfoKHX;
+
+typedef struct VkExportSemaphoreWin32HandleInfoKHX {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+    LPCWSTR                       name;
+} VkExportSemaphoreWin32HandleInfoKHX;
+
+typedef struct VkD3D12FenceSubmitInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           waitSemaphoreValuesCount;
+    const uint64_t*    pWaitSemaphoreValues;
+    uint32_t           signalSemaphoreValuesCount;
+    const uint64_t*    pSignalSemaphoreValues;
+} VkD3D12FenceSubmitInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHX)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHX(
+    VkDevice                                    device,
+    const VkImportSemaphoreWin32HandleInfoKHX*  pImportSemaphoreWin32HandleInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHX(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+    HANDLE*                                     pHandle);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHX */
+
+#define VK_KHX_external_semaphore_fd 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd"
+
+typedef struct VkImportSemaphoreFdInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkSemaphore                                 semaphore;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+    int                                         fd;
+} VkImportSemaphoreFdInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHX)(VkDevice device, const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHX(
+    VkDevice                                    device,
+    const VkImportSemaphoreFdInfoKHX*           pImportSemaphoreFdInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHX(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+    int*                                        pFd);
+#endif
+
+#define VK_NVX_device_generated_commands 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
+
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+
+typedef enum VkIndirectCommandsTokenTypeNVX {
+    VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+    VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+    VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+    VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+    VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+    VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1),
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsTokenTypeNVX;
+
+typedef enum VkObjectEntryTypeNVX {
+    VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+    VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+    VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+    VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+    VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+    VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX,
+    VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX,
+    VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1),
+    VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryTypeNVX;
+
+
+typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX {
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsLayoutUsageFlagBitsNVX;
+typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX;
+
+typedef enum VkObjectEntryUsageFlagBitsNVX {
+    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+    VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryUsageFlagBitsNVX;
+typedef VkFlags VkObjectEntryUsageFlagsNVX;
+
+typedef struct VkDeviceGeneratedCommandsFeaturesNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           computeBindingPointSupport;
+} VkDeviceGeneratedCommandsFeaturesNVX;
+
+typedef struct VkDeviceGeneratedCommandsLimitsNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           maxIndirectCommandsLayoutTokenCount;
+    uint32_t           maxObjectEntryCounts;
+    uint32_t           minSequenceCountBufferOffsetAlignment;
+    uint32_t           minSequenceIndexBufferOffsetAlignment;
+    uint32_t           minCommandsTokenBufferOffsetAlignment;
+} VkDeviceGeneratedCommandsLimitsNVX;
+
+typedef struct VkIndirectCommandsTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    VkBuffer                          buffer;
+    VkDeviceSize                      offset;
+} VkIndirectCommandsTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    uint32_t                          bindingUnit;
+    uint32_t                          dynamicCount;
+    uint32_t                          divisor;
+} VkIndirectCommandsLayoutTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutCreateInfoNVX {
+    VkStructureType                            sType;
+    const void*                                pNext;
+    VkPipelineBindPoint                        pipelineBindPoint;
+    VkIndirectCommandsLayoutUsageFlagsNVX      flags;
+    uint32_t                                   tokenCount;
+    const VkIndirectCommandsLayoutTokenNVX*    pTokens;
+} VkIndirectCommandsLayoutCreateInfoNVX;
+
+typedef struct VkCmdProcessCommandsInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkObjectTableNVX                     objectTable;
+    VkIndirectCommandsLayoutNVX          indirectCommandsLayout;
+    uint32_t                             indirectCommandsTokenCount;
+    const VkIndirectCommandsTokenNVX*    pIndirectCommandsTokens;
+    uint32_t                             maxSequencesCount;
+    VkCommandBuffer                      targetCommandBuffer;
+    VkBuffer                             sequencesCountBuffer;
+    VkDeviceSize                         sequencesCountOffset;
+    VkBuffer                             sequencesIndexBuffer;
+    VkDeviceSize                         sequencesIndexOffset;
+} VkCmdProcessCommandsInfoNVX;
+
+typedef struct VkCmdReserveSpaceForCommandsInfoNVX {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkObjectTableNVX               objectTable;
+    VkIndirectCommandsLayoutNVX    indirectCommandsLayout;
+    uint32_t                       maxSequencesCount;
+} VkCmdReserveSpaceForCommandsInfoNVX;
+
+typedef struct VkObjectTableCreateInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    uint32_t                             objectCount;
+    const VkObjectEntryTypeNVX*          pObjectEntryTypes;
+    const uint32_t*                      pObjectEntryCounts;
+    const VkObjectEntryUsageFlagsNVX*    pObjectEntryUsageFlags;
+    uint32_t                             maxUniformBuffersPerDescriptor;
+    uint32_t                             maxStorageBuffersPerDescriptor;
+    uint32_t                             maxStorageImagesPerDescriptor;
+    uint32_t                             maxSampledImagesPerDescriptor;
+    uint32_t                             maxPipelineLayouts;
+} VkObjectTableCreateInfoNVX;
+
+typedef struct VkObjectTableEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+} VkObjectTableEntryNVX;
+
+typedef struct VkObjectTablePipelineEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipeline                    pipeline;
+} VkObjectTablePipelineEntryNVX;
+
+typedef struct VkObjectTableDescriptorSetEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkDescriptorSet               descriptorSet;
+} VkObjectTableDescriptorSetEntryNVX;
+
+typedef struct VkObjectTableVertexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+} VkObjectTableVertexBufferEntryNVX;
+
+typedef struct VkObjectTableIndexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+    VkIndexType                   indexType;
+} VkObjectTableIndexBufferEntryNVX;
+
+typedef struct VkObjectTablePushConstantEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkShaderStageFlags            stageFlags;
+} VkObjectTablePushConstantEntryNVX;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable);
+typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const*    ppObjectTableEntries, const uint32_t* pObjectIndices);
+typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX(
+    VkDevice                                    device,
+    const VkObjectTableCreateInfoNVX*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkObjectTableNVX*                           pObjectTable);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+    VkPhysicalDevice                            physicalDevice,
+    VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
+    VkDeviceGeneratedCommandsLimitsNVX*         pLimits);
+#endif
+
+#define VK_NV_clip_space_w_scaling 1
+#define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
+#define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
+
+typedef struct VkViewportWScalingNV {
+    float    xcoeff;
+    float    ycoeff;
+} VkViewportWScalingNV;
+
+typedef struct VkPipelineViewportWScalingStateCreateInfoNV {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkBool32                       viewportWScalingEnable;
+    uint32_t                       viewportCount;
+    const VkViewportWScalingNV*    pViewportWScalings;
+} VkPipelineViewportWScalingStateCreateInfoNV;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstViewport,
+    uint32_t                                    viewportCount,
+    const VkViewportWScalingNV*                 pViewportWScalings);
+#endif
+
+#define VK_EXT_direct_mode_display 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
+
+typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayKHR                                display);
+#endif
+
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+#define VK_EXT_acquire_xlib_display 1
+#include <X11/extensions/Xrandr.h>
+
+#define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
+#define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
+
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display);
+typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    Display*                                    dpy,
+    VkDisplayKHR                                display);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    Display*                                    dpy,
+    RROutput                                    rrOutput,
+    VkDisplayKHR*                               pDisplay);
+#endif
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+#define VK_EXT_display_surface_counter 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+
+
+typedef enum VkSurfaceCounterFlagBitsEXT {
+    VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001,
+    VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkSurfaceCounterFlagBitsEXT;
+typedef VkFlags VkSurfaceCounterFlagsEXT;
+
+typedef struct VkSurfaceCapabilities2EXT {
+    VkStructureType                  sType;
+    void*                            pNext;
+    uint32_t                         minImageCount;
+    uint32_t                         maxImageCount;
+    VkExtent2D                       currentExtent;
+    VkExtent2D                       minImageExtent;
+    VkExtent2D                       maxImageExtent;
+    uint32_t                         maxImageArrayLayers;
+    VkSurfaceTransformFlagsKHR       supportedTransforms;
+    VkSurfaceTransformFlagBitsKHR    currentTransform;
+    VkCompositeAlphaFlagsKHR         supportedCompositeAlpha;
+    VkImageUsageFlags                supportedUsageFlags;
+    VkSurfaceCounterFlagsEXT         supportedSurfaceCounters;
+} VkSurfaceCapabilities2EXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities);
+#endif
+
+#define VK_EXT_display_control 1
+#define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control"
+
+
+typedef enum VkDisplayPowerStateEXT {
+    VK_DISPLAY_POWER_STATE_OFF_EXT = 0,
+    VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1,
+    VK_DISPLAY_POWER_STATE_ON_EXT = 2,
+    VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT,
+    VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT,
+    VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1),
+    VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayPowerStateEXT;
+
+typedef enum VkDeviceEventTypeEXT {
+    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0,
+    VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1),
+    VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDeviceEventTypeEXT;
+
+typedef enum VkDisplayEventTypeEXT {
+    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0,
+    VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1),
+    VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayEventTypeEXT;
+
+typedef struct VkDisplayPowerInfoEXT {
+    VkStructureType           sType;
+    const void*               pNext;
+    VkDisplayPowerStateEXT    powerState;
+} VkDisplayPowerInfoEXT;
+
+typedef struct VkDeviceEventInfoEXT {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkDeviceEventTypeEXT    deviceEvent;
+} VkDeviceEventInfoEXT;
+
+typedef struct VkDisplayEventInfoEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkDisplayEventTypeEXT    displayEvent;
+} VkDisplayEventInfoEXT;
+
+typedef struct VkSwapchainCounterCreateInfoEXT {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkSurfaceCounterFlagsEXT    surfaceCounters;
+} VkSwapchainCounterCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayPowerInfoEXT*                pDisplayPowerInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT(
+    VkDevice                                    device,
+    const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayEventInfoEXT*                pDisplayEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkSurfaceCounterFlagBitsEXT                 counter,
+    uint64_t*                                   pCounterValue);
+#endif
+
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+    uint64_t    refreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+    uint64_t    actualPresentTime;
+    uint64_t    earliestPresentTime;
+    uint64_t    presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+    VkStructureType               sType;
+    const void*                   pNext;
+    uint32_t                      swapchainCount;
+    const VkPresentTimeGOOGLE*    pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pPresentationTimingCount,
+    VkPastPresentationTimingGOOGLE*             pPresentationTimings);
+#endif
+
+#define VK_NV_sample_mask_override_coverage 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
+
+
+#define VK_NV_geometry_shader_passthrough 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
+
+
+#define VK_NV_viewport_array2 1
+#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
+
+
+#define VK_NVX_multiview_per_view_attributes 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
+
+typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           perViewPositionAllComponents;
+} VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX;
+
+
+
+#define VK_NV_viewport_swizzle 1
+#define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
+
+
+typedef enum VkViewportCoordinateSwizzleNV {
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1),
+    VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF
+} VkViewportCoordinateSwizzleNV;
+
+typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV;
+
+typedef struct VkViewportSwizzleNV {
+    VkViewportCoordinateSwizzleNV    x;
+    VkViewportCoordinateSwizzleNV    y;
+    VkViewportCoordinateSwizzleNV    z;
+    VkViewportCoordinateSwizzleNV    w;
+} VkViewportSwizzleNV;
+
+typedef struct VkPipelineViewportSwizzleStateCreateInfoNV {
+    VkStructureType                                sType;
+    const void*                                    pNext;
+    VkPipelineViewportSwizzleStateCreateFlagsNV    flags;
+    uint32_t                                       viewportCount;
+    const VkViewportSwizzleNV*                     pViewportSwizzles;
+} VkPipelineViewportSwizzleStateCreateInfoNV;
+
+
+
+#define VK_EXT_discard_rectangles 1
+#define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+#define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+
+typedef enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+    VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1),
+    VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDiscardRectangleModeEXT;
+
+typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT;
+
+typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxDiscardRectangles;
+} VkPhysicalDeviceDiscardRectanglePropertiesEXT;
+
+typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                  sType;
+    const void*                                      pNext;
+    VkPipelineDiscardRectangleStateCreateFlagsEXT    flags;
+    VkDiscardRectangleModeEXT                        discardRectangleMode;
+    uint32_t                                         discardRectangleCount;
+    const VkRect2D*                                  pDiscardRectangles;
+} VkPipelineDiscardRectangleStateCreateInfoEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstDiscardRectangle,
+    uint32_t                                    discardRectangleCount,
+    const VkRect2D*                             pDiscardRectangles);
+#endif
+
+#define VK_EXT_swapchain_colorspace 1
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 2
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
+
+
+#define VK_EXT_hdr_metadata 1
+#define VK_EXT_HDR_METADATA_SPEC_VERSION  1
+#define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
+
+typedef struct VkXYColorEXT {
+    float    x;
+    float    y;
+} VkXYColorEXT;
+
+typedef struct VkHdrMetadataEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkXYColorEXT       displayPrimaryRed;
+    VkXYColorEXT       displayPrimaryGreen;
+    VkXYColorEXT       displayPrimaryBlue;
+    VkXYColorEXT       whitePoint;
+    float              maxLuminance;
+    float              minLuminance;
+    float              maxContentLightLevel;
+    float              maxFrameAverageLightLevel;
+} VkHdrMetadataEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT(
+    VkDevice                                    device,
+    uint32_t                                    swapchainCount,
+    const VkSwapchainKHR*                       pSwapchains,
+    const VkHdrMetadataEXT*                     pMetadata);
+#endif
+
+#define VK_KHR_shared_presentable_image 1
+#define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
+#define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
+
+typedef struct VkSharedPresentSurfaceCapabilitiesKHR {
+    VkStructureType      sType;
+    void*                pNext;
+    VkImageUsageFlags    sharedPresentSupportedUsageFlags;
+} VkSharedPresentSurfaceCapabilitiesKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain);
+#endif
+
+#define VK_KHR_get_surface_capabilities2 1
+#define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
+
+typedef struct VkPhysicalDeviceSurfaceInfo2KHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSurfaceKHR       surface;
+} VkPhysicalDeviceSurfaceInfo2KHR;
+
+typedef struct VkSurfaceCapabilities2KHR {
+    VkStructureType             sType;
+    void*                       pNext;
+    VkSurfaceCapabilitiesKHR    surfaceCapabilities;
+} VkSurfaceCapabilities2KHR;
+
+typedef struct VkSurfaceFormat2KHR {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkSurfaceFormatKHR    surfaceFormat;
+} VkSurfaceFormat2KHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
+    VkSurfaceCapabilities2KHR*                  pSurfaceCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormats2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
+    uint32_t*                                   pSurfaceFormatCount,
+    VkSurfaceFormat2KHR*                        pSurfaceFormats);
+#endif
+
+
+
+#ifdef VK_USE_PLATFORM_IOS_MVK
+#define VK_MVK_ios_surface 1
+#define VK_MVK_IOS_SURFACE_SPEC_VERSION   2
+#define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
+
+typedef VkFlags VkIOSSurfaceCreateFlagsMVK;
+
+typedef struct VkIOSSurfaceCreateInfoMVK {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkIOSSurfaceCreateFlagsMVK    flags;
+    const void*                   pView;
+} VkIOSSurfaceCreateInfoMVK;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK(
+    VkInstance                                  instance,
+    const VkIOSSurfaceCreateInfoMVK*            pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_IOS_MVK */
+
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+#define VK_MVK_macos_surface 1
+#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2
+#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
+
+typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
+
+typedef struct VkMacOSSurfaceCreateInfoMVK {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkMacOSSurfaceCreateFlagsMVK    flags;
+    const void*                     pView;
+} VkMacOSSurfaceCreateInfoMVK;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(
+    VkInstance                                  instance,
+    const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_MACOS_MVK */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
new file mode 100644
index 0000000..6149894
--- /dev/null
+++ b/vulkan/libvulkan/Android.bp
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Headers module is in frameworks/native/vulkan/Android.bp.
+ndk_library {
+    name: "libvulkan",
+    symbol_file: "libvulkan.map.txt",
+    first_version: "24",
+    unversioned_until: "current",
+}
+
+cc_library_shared {
+    name: "libvulkan",
+    clang: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+
+    cflags: [
+        "-DLOG_TAG=\"vulkan\"",
+        "-DVK_USE_PLATFORM_ANDROID_KHR",
+        "-DVK_NO_PROTOTYPES",
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-switch-enum",
+        "-Wno-undef",
+
+        //"-DLOG_NDEBUG=0",
+    ],
+
+    cppflags: [
+        "-std=c++14",
+        "-Wno-c99-extensions",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-exit-time-destructors",
+        "-Wno-float-equal",
+        "-Wno-global-constructors",
+        "-Wno-zero-length-array",
+    ],
+
+    srcs: [
+        "api.cpp",
+        "api_gen.cpp",
+        "debug_report.cpp",
+        "driver.cpp",
+        "driver_gen.cpp",
+        "layers_extensions.cpp",
+        "stubhal.cpp",
+        "swapchain.cpp",
+        "vulkan_loader_data.cpp",
+    ],
+
+    export_header_lib_headers: ["vulkan_headers"],
+    header_libs: [
+        "vulkan_headers",
+    ],
+    shared_libs: [
+        "libziparchive",
+        "libhardware",
+        "libsync",
+        "libbase",
+        "liblog",
+        "libui",
+        "libutils",
+        "libcutils",
+        "libz",
+        "libnativewindow",
+    ],
+    static_libs: ["libgrallocusage"],
+}
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
deleted file mode 100644
index b7d6791..0000000
--- a/vulkan/libvulkan/Android.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-LOCAL_CFLAGS := -DLOG_TAG=\"vulkan\" \
-	-DVK_USE_PLATFORM_ANDROID_KHR \
-	-DVK_NO_PROTOTYPES \
-	-std=c99 -fvisibility=hidden -fstrict-aliasing \
-	-Weverything -Werror \
-	-Wno-padded \
-	-Wno-switch-enum \
-	-Wno-undef
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++14 \
-	-Wno-c99-extensions \
-	-Wno-c++98-compat-pedantic \
-	-Wno-exit-time-destructors \
-	-Wno-global-constructors \
-	-Wno-zero-length-array
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include \
-	system/core/libsync/include
-
-LOCAL_SRC_FILES := \
-	api.cpp \
-	api_gen.cpp \
-	debug_report.cpp \
-	driver.cpp \
-	driver_gen.cpp \
-	layers_extensions.cpp \
-	stubhal.cpp \
-	swapchain.cpp \
-	vulkan_loader_data.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_STATIC_LIBRARIES := libziparchive
-LOCAL_SHARED_LIBRARIES := libgui libhardware libsync libbase liblog libutils libcutils libz
-
-LOCAL_MODULE := libvulkan
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index b699fe9..e05ca5a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -23,10 +23,12 @@
 
 #include <stdlib.h>
 #include <string.h>
+
 #include <algorithm>
 #include <mutex>
 #include <new>
 #include <utility>
+
 #include <cutils/properties.h>
 #include <log/log.h>
 
@@ -1048,7 +1050,8 @@
 VkResult LayerChain::CreateInstance(const VkInstanceCreateInfo* create_info,
                                     const VkAllocationCallbacks* allocator,
                                     VkInstance* instance_out) {
-    LayerChain chain(true, driver::DebugReportLogger(*create_info),
+    const driver::DebugReportLogger logger(*create_info);
+    LayerChain chain(true, logger,
                      (allocator) ? *allocator : driver::GetDefaultAllocator());
 
     VkResult result = chain.ActivateLayers(create_info->ppEnabledLayerNames,
@@ -1073,8 +1076,9 @@
                                   const VkDeviceCreateInfo* create_info,
                                   const VkAllocationCallbacks* allocator,
                                   VkDevice* dev_out) {
+    const driver::DebugReportLogger logger = driver::Logger(physical_dev);
     LayerChain chain(
-        false, driver::Logger(physical_dev),
+        false, logger,
         (allocator) ? *allocator : driver::GetData(physical_dev).allocator);
 
     VkResult result = chain.ActivateLayers(
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 0a1dda2..54c77f1 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -17,7 +17,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.
 
 #include <string.h>
+
 #include <algorithm>
+
 #include <log/log.h>
 
 // to catch mismatches between vulkan.h and this file
@@ -29,11 +31,11 @@
 
 #define UNLIKELY(expr) __builtin_expect((expr), 0)
 
-#define INIT_PROC(obj, proc)                                           \
+#define INIT_PROC(required, obj, proc)                                 \
     do {                                                               \
         data.dispatch.proc =                                           \
             reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
-        if (UNLIKELY(!data.dispatch.proc)) {                           \
+        if (UNLIKELY(required && !data.dispatch.proc)) {               \
             ALOGE("missing " #obj " proc: vk" #proc);                  \
             success = false;                                           \
         }                                                              \
@@ -41,10 +43,10 @@
 
 // Exported extension functions may be invoked even when their extensions
 // are disabled.  Dispatch to stubs when that happens.
-#define INIT_PROC_EXT(ext, obj, proc)            \
+#define INIT_PROC_EXT(ext, required, obj, proc)  \
     do {                                         \
         if (extensions[driver::ProcHook::ext])   \
-            INIT_PROC(obj, proc);                \
+            INIT_PROC(required, obj, proc);      \
         else                                     \
             data.dispatch.proc = disabled##proc; \
     } while (0)
@@ -108,7 +110,7 @@
 
 // clang-format on
 
-}  // anonymous
+}  // namespace
 
 bool InitDispatchTable(
     VkInstance instance,
@@ -118,24 +120,24 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(instance, DestroyInstance);
-    INIT_PROC(instance, EnumeratePhysicalDevices);
-    INIT_PROC(instance, GetInstanceProcAddr);
-    INIT_PROC(instance, GetPhysicalDeviceProperties);
-    INIT_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
-    INIT_PROC(instance, GetPhysicalDeviceMemoryProperties);
-    INIT_PROC(instance, GetPhysicalDeviceFeatures);
-    INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
-    INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
-    INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceExtensionProperties);
-    INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
-    INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
-    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceSupportKHR);
-    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
-    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceFormatsKHR);
-    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfacePresentModesKHR);
-    INIT_PROC_EXT(KHR_android_surface, instance, CreateAndroidSurfaceKHR);
+    INIT_PROC(true, instance, DestroyInstance);
+    INIT_PROC(true, instance, EnumeratePhysicalDevices);
+    INIT_PROC(true, instance, GetInstanceProcAddr);
+    INIT_PROC(true, instance, GetPhysicalDeviceProperties);
+    INIT_PROC(true, instance, GetPhysicalDeviceQueueFamilyProperties);
+    INIT_PROC(true, instance, GetPhysicalDeviceMemoryProperties);
+    INIT_PROC(true, instance, GetPhysicalDeviceFeatures);
+    INIT_PROC(true, instance, GetPhysicalDeviceFormatProperties);
+    INIT_PROC(true, instance, GetPhysicalDeviceImageFormatProperties);
+    INIT_PROC(true, instance, CreateDevice);
+    INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
+    INIT_PROC(true, instance, GetPhysicalDeviceSparseImageFormatProperties);
+    INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR);
+    INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR);
     // clang-format on
 
     return success;
@@ -149,132 +151,132 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(dev, GetDeviceProcAddr);
-    INIT_PROC(dev, DestroyDevice);
-    INIT_PROC(dev, GetDeviceQueue);
-    INIT_PROC(dev, QueueSubmit);
-    INIT_PROC(dev, QueueWaitIdle);
-    INIT_PROC(dev, DeviceWaitIdle);
-    INIT_PROC(dev, AllocateMemory);
-    INIT_PROC(dev, FreeMemory);
-    INIT_PROC(dev, MapMemory);
-    INIT_PROC(dev, UnmapMemory);
-    INIT_PROC(dev, FlushMappedMemoryRanges);
-    INIT_PROC(dev, InvalidateMappedMemoryRanges);
-    INIT_PROC(dev, GetDeviceMemoryCommitment);
-    INIT_PROC(dev, GetBufferMemoryRequirements);
-    INIT_PROC(dev, BindBufferMemory);
-    INIT_PROC(dev, GetImageMemoryRequirements);
-    INIT_PROC(dev, BindImageMemory);
-    INIT_PROC(dev, GetImageSparseMemoryRequirements);
-    INIT_PROC(dev, QueueBindSparse);
-    INIT_PROC(dev, CreateFence);
-    INIT_PROC(dev, DestroyFence);
-    INIT_PROC(dev, ResetFences);
-    INIT_PROC(dev, GetFenceStatus);
-    INIT_PROC(dev, WaitForFences);
-    INIT_PROC(dev, CreateSemaphore);
-    INIT_PROC(dev, DestroySemaphore);
-    INIT_PROC(dev, CreateEvent);
-    INIT_PROC(dev, DestroyEvent);
-    INIT_PROC(dev, GetEventStatus);
-    INIT_PROC(dev, SetEvent);
-    INIT_PROC(dev, ResetEvent);
-    INIT_PROC(dev, CreateQueryPool);
-    INIT_PROC(dev, DestroyQueryPool);
-    INIT_PROC(dev, GetQueryPoolResults);
-    INIT_PROC(dev, CreateBuffer);
-    INIT_PROC(dev, DestroyBuffer);
-    INIT_PROC(dev, CreateBufferView);
-    INIT_PROC(dev, DestroyBufferView);
-    INIT_PROC(dev, CreateImage);
-    INIT_PROC(dev, DestroyImage);
-    INIT_PROC(dev, GetImageSubresourceLayout);
-    INIT_PROC(dev, CreateImageView);
-    INIT_PROC(dev, DestroyImageView);
-    INIT_PROC(dev, CreateShaderModule);
-    INIT_PROC(dev, DestroyShaderModule);
-    INIT_PROC(dev, CreatePipelineCache);
-    INIT_PROC(dev, DestroyPipelineCache);
-    INIT_PROC(dev, GetPipelineCacheData);
-    INIT_PROC(dev, MergePipelineCaches);
-    INIT_PROC(dev, CreateGraphicsPipelines);
-    INIT_PROC(dev, CreateComputePipelines);
-    INIT_PROC(dev, DestroyPipeline);
-    INIT_PROC(dev, CreatePipelineLayout);
-    INIT_PROC(dev, DestroyPipelineLayout);
-    INIT_PROC(dev, CreateSampler);
-    INIT_PROC(dev, DestroySampler);
-    INIT_PROC(dev, CreateDescriptorSetLayout);
-    INIT_PROC(dev, DestroyDescriptorSetLayout);
-    INIT_PROC(dev, CreateDescriptorPool);
-    INIT_PROC(dev, DestroyDescriptorPool);
-    INIT_PROC(dev, ResetDescriptorPool);
-    INIT_PROC(dev, AllocateDescriptorSets);
-    INIT_PROC(dev, FreeDescriptorSets);
-    INIT_PROC(dev, UpdateDescriptorSets);
-    INIT_PROC(dev, CreateFramebuffer);
-    INIT_PROC(dev, DestroyFramebuffer);
-    INIT_PROC(dev, CreateRenderPass);
-    INIT_PROC(dev, DestroyRenderPass);
-    INIT_PROC(dev, GetRenderAreaGranularity);
-    INIT_PROC(dev, CreateCommandPool);
-    INIT_PROC(dev, DestroyCommandPool);
-    INIT_PROC(dev, ResetCommandPool);
-    INIT_PROC(dev, AllocateCommandBuffers);
-    INIT_PROC(dev, FreeCommandBuffers);
-    INIT_PROC(dev, BeginCommandBuffer);
-    INIT_PROC(dev, EndCommandBuffer);
-    INIT_PROC(dev, ResetCommandBuffer);
-    INIT_PROC(dev, CmdBindPipeline);
-    INIT_PROC(dev, CmdSetViewport);
-    INIT_PROC(dev, CmdSetScissor);
-    INIT_PROC(dev, CmdSetLineWidth);
-    INIT_PROC(dev, CmdSetDepthBias);
-    INIT_PROC(dev, CmdSetBlendConstants);
-    INIT_PROC(dev, CmdSetDepthBounds);
-    INIT_PROC(dev, CmdSetStencilCompareMask);
-    INIT_PROC(dev, CmdSetStencilWriteMask);
-    INIT_PROC(dev, CmdSetStencilReference);
-    INIT_PROC(dev, CmdBindDescriptorSets);
-    INIT_PROC(dev, CmdBindIndexBuffer);
-    INIT_PROC(dev, CmdBindVertexBuffers);
-    INIT_PROC(dev, CmdDraw);
-    INIT_PROC(dev, CmdDrawIndexed);
-    INIT_PROC(dev, CmdDrawIndirect);
-    INIT_PROC(dev, CmdDrawIndexedIndirect);
-    INIT_PROC(dev, CmdDispatch);
-    INIT_PROC(dev, CmdDispatchIndirect);
-    INIT_PROC(dev, CmdCopyBuffer);
-    INIT_PROC(dev, CmdCopyImage);
-    INIT_PROC(dev, CmdBlitImage);
-    INIT_PROC(dev, CmdCopyBufferToImage);
-    INIT_PROC(dev, CmdCopyImageToBuffer);
-    INIT_PROC(dev, CmdUpdateBuffer);
-    INIT_PROC(dev, CmdFillBuffer);
-    INIT_PROC(dev, CmdClearColorImage);
-    INIT_PROC(dev, CmdClearDepthStencilImage);
-    INIT_PROC(dev, CmdClearAttachments);
-    INIT_PROC(dev, CmdResolveImage);
-    INIT_PROC(dev, CmdSetEvent);
-    INIT_PROC(dev, CmdResetEvent);
-    INIT_PROC(dev, CmdWaitEvents);
-    INIT_PROC(dev, CmdPipelineBarrier);
-    INIT_PROC(dev, CmdBeginQuery);
-    INIT_PROC(dev, CmdEndQuery);
-    INIT_PROC(dev, CmdResetQueryPool);
-    INIT_PROC(dev, CmdWriteTimestamp);
-    INIT_PROC(dev, CmdCopyQueryPoolResults);
-    INIT_PROC(dev, CmdPushConstants);
-    INIT_PROC(dev, CmdBeginRenderPass);
-    INIT_PROC(dev, CmdNextSubpass);
-    INIT_PROC(dev, CmdEndRenderPass);
-    INIT_PROC(dev, CmdExecuteCommands);
-    INIT_PROC_EXT(KHR_swapchain, dev, CreateSwapchainKHR);
-    INIT_PROC_EXT(KHR_swapchain, dev, DestroySwapchainKHR);
-    INIT_PROC_EXT(KHR_swapchain, dev, GetSwapchainImagesKHR);
-    INIT_PROC_EXT(KHR_swapchain, dev, AcquireNextImageKHR);
-    INIT_PROC_EXT(KHR_swapchain, dev, QueuePresentKHR);
+    INIT_PROC(true, dev, GetDeviceProcAddr);
+    INIT_PROC(true, dev, DestroyDevice);
+    INIT_PROC(true, dev, GetDeviceQueue);
+    INIT_PROC(true, dev, QueueSubmit);
+    INIT_PROC(true, dev, QueueWaitIdle);
+    INIT_PROC(true, dev, DeviceWaitIdle);
+    INIT_PROC(true, dev, AllocateMemory);
+    INIT_PROC(true, dev, FreeMemory);
+    INIT_PROC(true, dev, MapMemory);
+    INIT_PROC(true, dev, UnmapMemory);
+    INIT_PROC(true, dev, FlushMappedMemoryRanges);
+    INIT_PROC(true, dev, InvalidateMappedMemoryRanges);
+    INIT_PROC(true, dev, GetDeviceMemoryCommitment);
+    INIT_PROC(true, dev, GetBufferMemoryRequirements);
+    INIT_PROC(true, dev, BindBufferMemory);
+    INIT_PROC(true, dev, GetImageMemoryRequirements);
+    INIT_PROC(true, dev, BindImageMemory);
+    INIT_PROC(true, dev, GetImageSparseMemoryRequirements);
+    INIT_PROC(true, dev, QueueBindSparse);
+    INIT_PROC(true, dev, CreateFence);
+    INIT_PROC(true, dev, DestroyFence);
+    INIT_PROC(true, dev, ResetFences);
+    INIT_PROC(true, dev, GetFenceStatus);
+    INIT_PROC(true, dev, WaitForFences);
+    INIT_PROC(true, dev, CreateSemaphore);
+    INIT_PROC(true, dev, DestroySemaphore);
+    INIT_PROC(true, dev, CreateEvent);
+    INIT_PROC(true, dev, DestroyEvent);
+    INIT_PROC(true, dev, GetEventStatus);
+    INIT_PROC(true, dev, SetEvent);
+    INIT_PROC(true, dev, ResetEvent);
+    INIT_PROC(true, dev, CreateQueryPool);
+    INIT_PROC(true, dev, DestroyQueryPool);
+    INIT_PROC(true, dev, GetQueryPoolResults);
+    INIT_PROC(true, dev, CreateBuffer);
+    INIT_PROC(true, dev, DestroyBuffer);
+    INIT_PROC(true, dev, CreateBufferView);
+    INIT_PROC(true, dev, DestroyBufferView);
+    INIT_PROC(true, dev, CreateImage);
+    INIT_PROC(true, dev, DestroyImage);
+    INIT_PROC(true, dev, GetImageSubresourceLayout);
+    INIT_PROC(true, dev, CreateImageView);
+    INIT_PROC(true, dev, DestroyImageView);
+    INIT_PROC(true, dev, CreateShaderModule);
+    INIT_PROC(true, dev, DestroyShaderModule);
+    INIT_PROC(true, dev, CreatePipelineCache);
+    INIT_PROC(true, dev, DestroyPipelineCache);
+    INIT_PROC(true, dev, GetPipelineCacheData);
+    INIT_PROC(true, dev, MergePipelineCaches);
+    INIT_PROC(true, dev, CreateGraphicsPipelines);
+    INIT_PROC(true, dev, CreateComputePipelines);
+    INIT_PROC(true, dev, DestroyPipeline);
+    INIT_PROC(true, dev, CreatePipelineLayout);
+    INIT_PROC(true, dev, DestroyPipelineLayout);
+    INIT_PROC(true, dev, CreateSampler);
+    INIT_PROC(true, dev, DestroySampler);
+    INIT_PROC(true, dev, CreateDescriptorSetLayout);
+    INIT_PROC(true, dev, DestroyDescriptorSetLayout);
+    INIT_PROC(true, dev, CreateDescriptorPool);
+    INIT_PROC(true, dev, DestroyDescriptorPool);
+    INIT_PROC(true, dev, ResetDescriptorPool);
+    INIT_PROC(true, dev, AllocateDescriptorSets);
+    INIT_PROC(true, dev, FreeDescriptorSets);
+    INIT_PROC(true, dev, UpdateDescriptorSets);
+    INIT_PROC(true, dev, CreateFramebuffer);
+    INIT_PROC(true, dev, DestroyFramebuffer);
+    INIT_PROC(true, dev, CreateRenderPass);
+    INIT_PROC(true, dev, DestroyRenderPass);
+    INIT_PROC(true, dev, GetRenderAreaGranularity);
+    INIT_PROC(true, dev, CreateCommandPool);
+    INIT_PROC(true, dev, DestroyCommandPool);
+    INIT_PROC(true, dev, ResetCommandPool);
+    INIT_PROC(true, dev, AllocateCommandBuffers);
+    INIT_PROC(true, dev, FreeCommandBuffers);
+    INIT_PROC(true, dev, BeginCommandBuffer);
+    INIT_PROC(true, dev, EndCommandBuffer);
+    INIT_PROC(true, dev, ResetCommandBuffer);
+    INIT_PROC(true, dev, CmdBindPipeline);
+    INIT_PROC(true, dev, CmdSetViewport);
+    INIT_PROC(true, dev, CmdSetScissor);
+    INIT_PROC(true, dev, CmdSetLineWidth);
+    INIT_PROC(true, dev, CmdSetDepthBias);
+    INIT_PROC(true, dev, CmdSetBlendConstants);
+    INIT_PROC(true, dev, CmdSetDepthBounds);
+    INIT_PROC(true, dev, CmdSetStencilCompareMask);
+    INIT_PROC(true, dev, CmdSetStencilWriteMask);
+    INIT_PROC(true, dev, CmdSetStencilReference);
+    INIT_PROC(true, dev, CmdBindDescriptorSets);
+    INIT_PROC(true, dev, CmdBindIndexBuffer);
+    INIT_PROC(true, dev, CmdBindVertexBuffers);
+    INIT_PROC(true, dev, CmdDraw);
+    INIT_PROC(true, dev, CmdDrawIndexed);
+    INIT_PROC(true, dev, CmdDrawIndirect);
+    INIT_PROC(true, dev, CmdDrawIndexedIndirect);
+    INIT_PROC(true, dev, CmdDispatch);
+    INIT_PROC(true, dev, CmdDispatchIndirect);
+    INIT_PROC(true, dev, CmdCopyBuffer);
+    INIT_PROC(true, dev, CmdCopyImage);
+    INIT_PROC(true, dev, CmdBlitImage);
+    INIT_PROC(true, dev, CmdCopyBufferToImage);
+    INIT_PROC(true, dev, CmdCopyImageToBuffer);
+    INIT_PROC(true, dev, CmdUpdateBuffer);
+    INIT_PROC(true, dev, CmdFillBuffer);
+    INIT_PROC(true, dev, CmdClearColorImage);
+    INIT_PROC(true, dev, CmdClearDepthStencilImage);
+    INIT_PROC(true, dev, CmdClearAttachments);
+    INIT_PROC(true, dev, CmdResolveImage);
+    INIT_PROC(true, dev, CmdSetEvent);
+    INIT_PROC(true, dev, CmdResetEvent);
+    INIT_PROC(true, dev, CmdWaitEvents);
+    INIT_PROC(true, dev, CmdPipelineBarrier);
+    INIT_PROC(true, dev, CmdBeginQuery);
+    INIT_PROC(true, dev, CmdEndQuery);
+    INIT_PROC(true, dev, CmdResetQueryPool);
+    INIT_PROC(true, dev, CmdWriteTimestamp);
+    INIT_PROC(true, dev, CmdCopyQueryPoolResults);
+    INIT_PROC(true, dev, CmdPushConstants);
+    INIT_PROC(true, dev, CmdBeginRenderPass);
+    INIT_PROC(true, dev, CmdNextSubpass);
+    INIT_PROC(true, dev, CmdEndRenderPass);
+    INIT_PROC(true, dev, CmdExecuteCommands);
+    INIT_PROC_EXT(KHR_swapchain, true, dev, CreateSwapchainKHR);
+    INIT_PROC_EXT(KHR_swapchain, true, dev, DestroySwapchainKHR);
+    INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR);
+    INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR);
+    INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR);
     // clang-format on
 
     return success;
@@ -387,14 +389,14 @@
 VKAPI_ATTR void CmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 VKAPI_ATTR void CmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 VKAPI_ATTR void CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 VKAPI_ATTR void CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 VKAPI_ATTR void CmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
 VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -449,16 +451,31 @@
         "vkEnumerateDeviceLayerProperties",
         "vkEnumerateInstanceExtensionProperties",
         "vkEnumerateInstanceLayerProperties",
+        "vkEnumeratePhysicalDeviceGroupsKHX",
         "vkEnumeratePhysicalDevices",
         "vkGetInstanceProcAddr",
+        "vkGetPhysicalDeviceExternalBufferPropertiesKHX",
+        "vkGetPhysicalDeviceExternalImageFormatPropertiesNV",
+        "vkGetPhysicalDeviceExternalSemaphorePropertiesKHX",
         "vkGetPhysicalDeviceFeatures",
+        "vkGetPhysicalDeviceFeatures2KHR",
         "vkGetPhysicalDeviceFormatProperties",
+        "vkGetPhysicalDeviceFormatProperties2KHR",
+        "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
         "vkGetPhysicalDeviceImageFormatProperties",
+        "vkGetPhysicalDeviceImageFormatProperties2KHR",
         "vkGetPhysicalDeviceMemoryProperties",
+        "vkGetPhysicalDeviceMemoryProperties2KHR",
+        "vkGetPhysicalDevicePresentRectanglesKHX",
         "vkGetPhysicalDeviceProperties",
+        "vkGetPhysicalDeviceProperties2KHR",
         "vkGetPhysicalDeviceQueueFamilyProperties",
+        "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
         "vkGetPhysicalDeviceSparseImageFormatProperties",
+        "vkGetPhysicalDeviceSparseImageFormatProperties2KHR",
+        "vkGetPhysicalDeviceSurfaceCapabilities2KHR",
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
+        "vkGetPhysicalDeviceSurfaceFormats2KHR",
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
@@ -1047,8 +1064,8 @@
     GetData(commandBuffer).dispatch.CmdDrawIndexedIndirect(commandBuffer, buffer, offset, drawCount, stride);
 }
 
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
-    GetData(commandBuffer).dispatch.CmdDispatch(commandBuffer, x, y, z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    GetData(commandBuffer).dispatch.CmdDispatch(commandBuffer, groupCountX, groupCountY, groupCountZ);
 }
 
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) {
@@ -1075,7 +1092,7 @@
     GetData(commandBuffer).dispatch.CmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
 }
 
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
     GetData(commandBuffer).dispatch.CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
 }
 
@@ -1760,8 +1777,8 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
-    vulkan::api::CmdDispatch(commandBuffer, x, y, z);
+VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    vulkan::api::CmdDispatch(commandBuffer, groupCountX, groupCountY, groupCountZ);
 }
 
 __attribute__((visibility("default")))
@@ -1795,7 +1812,7 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
     vulkan::api::CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
 }
 
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 7f8d274..3e50fda 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -19,8 +19,8 @@
 #ifndef LIBVULKAN_API_GEN_H
 #define LIBVULKAN_API_GEN_H
 
-#include <bitset>
 #include <vulkan/vulkan.h>
+#include <bitset>
 #include "driver_gen.h"
 
 namespace vulkan {
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index f9a4670..5bbe116 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -91,7 +91,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.

 #include <string.h>

 #include <algorithm>

 #include <log/log.h>

 // to catch mismatches between vulkan.h and this file
@@ -270,7 +272,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.

 #include <string.h>

 #include <algorithm>

 #include <log/log.h>

 #include "driver.h"
@@ -315,7 +319,7 @@
 }

 ProcHook::Extension GetProcHookExtension(const char* name) {
-  {{$exts := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+  {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}}
   // clang-format off
   {{range $e := $exts}}
     if (strcmp(name, "{{$e}}") == 0) return ProcHook::{{TrimPrefix "VK_" $e}};
@@ -389,10 +393,10 @@
 {{define "C++.DefineInitProcMacro"}}
   #define UNLIKELY(expr) __builtin_expect((expr), 0)

-  #define INIT_PROC(obj, proc) do {                             \
+  #define INIT_PROC(required, obj, proc) do {                   \
       data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>(       \
               get_proc(obj, "vk" # proc));                      \
-      if (UNLIKELY(!data.{{$}}.proc)) {                         \
+      if (UNLIKELY(required && !data.{{$}}.proc)) {             \
           ALOGE("missing " # obj " proc: vk" # proc);           \
           success = false;                                      \
       }                                                         \
@@ -415,6 +419,8 @@
     INIT_PROC(§
   {{end}}
 
+  {{if GetAnnotation $ "optional"}}false{{else}}true{{end}}, §
+
   {{if (Macro "IsInstanceDispatched" $)}}
     instance, §
   {{else}}
@@ -490,9 +496,9 @@
 {{define "api.C++.DefineInitProcExtMacro"}}
   // Exported extension functions may be invoked even when their extensions
   // are disabled.  Dispatch to stubs when that happens.
-  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
       if (extensions[driver::ProcHook::ext])                    \
-        INIT_PROC(obj, proc);                                   \
+        INIT_PROC(required, obj, proc);                         \
       else                                                      \
         data.dispatch.proc = disabled ## proc;                  \
   } while(0)
@@ -676,9 +682,26 @@
 {{define "driver.InterceptedExtensions"}}
 VK_ANDROID_native_buffer
 VK_EXT_debug_report
+VK_EXT_hdr_metadata
+VK_EXT_swapchain_colorspace
+VK_GOOGLE_display_timing
 VK_KHR_android_surface
+VK_KHR_incremental_present
+VK_KHR_shared_presentable_image
 VK_KHR_surface
 VK_KHR_swapchain
+VK_KHR_get_surface_capabilities2
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits a list of extensions known to vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.KnownExtensions"}}
+{{Macro "driver.InterceptedExtensions"}}
+VK_KHR_get_physical_device_properties2
 {{end}}
 
 
@@ -766,7 +789,7 @@
       };
 
       enum Extension {
-        {{$exts := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+        {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}}
         {{range $e := $exts}}
           {{TrimPrefix "VK_" $e}},
         {{end}}
@@ -792,9 +815,9 @@
 -------------------------------------------------------------------------------
 */}}
 {{define "driver.C++.DefineInitProcExtMacro"}}
-  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
       if (extensions[ProcHook::ext])                            \
-        INIT_PROC(obj, proc);                                   \
+        INIT_PROC(required, obj, proc);                         \
   } while(0)
 {{end}}
 
@@ -954,6 +977,8 @@
     {{else if eq $.Name "vkCreateImage"}}true
     {{else if eq $.Name "vkDestroyImage"}}true
 
+    {{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true
+    {{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true
     {{end}}
 
     {{$ext := GetAnnotation $ "extension"}}
@@ -1105,19 +1130,32 @@
   {{$ext := index $.Arguments 0}}
   {{     if eq $ext "VK_KHR_display"}}true
   {{else if eq $ext "VK_KHR_display_swapchain"}}true
-  {{else if eq $ext "VK_KHR_xlib_surface"}}true
-  {{else if eq $ext "VK_KHR_xcb_surface"}}true
-  {{else if eq $ext "VK_KHR_wayland_surface"}}true
   {{else if eq $ext "VK_KHR_mir_surface"}}true
+  {{else if eq $ext "VK_KHR_xcb_surface"}}true
+  {{else if eq $ext "VK_KHR_xlib_surface"}}true
+  {{else if eq $ext "VK_KHR_wayland_surface"}}true
   {{else if eq $ext "VK_KHR_win32_surface"}}true
+  {{else if eq $ext "VK_KHX_external_memory_win32"}}true
+  {{else if eq $ext "VK_KHX_win32_keyed_mutex"}}true
+  {{else if eq $ext "VK_KHX_external_semaphore_win32"}}true
+  {{else if eq $ext "VK_EXT_acquire_xlib_display"}}true
+  {{else if eq $ext "VK_EXT_direct_mode_display"}}true
+  {{else if eq $ext "VK_EXT_display_surface_counter"}}true
+  {{else if eq $ext "VK_EXT_display_control"}}true
+  {{else if eq $ext "VK_MVK_ios_surface"}}true
+  {{else if eq $ext "VK_MVK_macos_surface"}}true
+  {{else if eq $ext "VK_NN_vi_surface"}}true
+  {{else if eq $ext "VK_NV_external_memory_win32"}}true
+  {{else if eq $ext "VK_NV_win32_keyed_mutex"}}true
   {{end}}
 {{end}}
 
 
 {{/*
 ------------------------------------------------------------------------------
-  Reports whether an extension is implemented entirely by the loader,
-  so drivers should not enumerate it.
+  Reports whether an extension has functions exported by the loader.
+  E.g. applications can directly link to an extension function.
+  Currently only support WSI extensions this way.
 ------------------------------------------------------------------------------
 */}}
 {{define "IsExtensionExported"}}
diff --git a/vulkan/libvulkan/debug_report.cpp b/vulkan/libvulkan/debug_report.cpp
index 0c2f138..40ba1e5 100644
--- a/vulkan/libvulkan/debug_report.cpp
+++ b/vulkan/libvulkan/debug_report.cpp
@@ -46,7 +46,8 @@
         Node* prev = &head_;
         while (prev && prev->next != node)
             prev = prev->next;
-        prev->next = node->next;
+        if (prev)
+            prev->next = node->next;
     }
 
     allocator.pfnFree(allocator.pUserData, node);
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index be9b645..3d8bd50 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -85,9 +85,9 @@
 
 class DebugReportLogger {
    public:
-    DebugReportLogger(const VkInstanceCreateInfo& info)
+    explicit DebugReportLogger(const VkInstanceCreateInfo& info)
         : instance_pnext_(info.pNext), callbacks_(nullptr) {}
-    DebugReportLogger(const DebugReportCallbackList& callbacks)
+    explicit DebugReportLogger(const DebugReportCallbackList& callbacks)
         : instance_pnext_(nullptr), callbacks_(&callbacks) {}
 
     void Message(VkDebugReportFlagsEXT flags,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 741a0ce..6f425f5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -14,22 +14,31 @@
  * limitations under the License.
  */
 
+#include <malloc.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/prctl.h>
+
 #include <algorithm>
 #include <array>
 #include <dlfcn.h>
 #include <new>
-#include <malloc.h>
-#include <sys/prctl.h>
+
+#include <log/log.h>
 
 #include <android/dlext.h>
 #include <cutils/properties.h>
-#include <gui/GraphicsEnv.h>
+#include <ui/GraphicsEnv.h>
+#include <utils/Vector.h>
 
 #include "driver.h"
 #include "stubhal.h"
 
+// TODO(b/37049319) Get this from a header once one exists
+extern "C" {
+android_namespace_t* android_get_exported_namespace(const char*);
+}
+
 // #define ENABLE_ALLOC_CALLSTACKS 1
 #if ENABLE_ALLOC_CALLSTACKS
 #include <utils/CallStack.h>
@@ -143,14 +152,12 @@
     "ro.board.platform",
 }};
 
-int LoadUpdatedDriver(const hw_module_t** module) {
+int LoadDriver(android_namespace_t* library_namespace,
+               const hwvulkan_module_t** module) {
     const android_dlextinfo dlextinfo = {
         .flags = ANDROID_DLEXT_USE_NAMESPACE,
-        .library_namespace = android::GraphicsEnv::getInstance().getDriverNamespace(),
+        .library_namespace = library_namespace,
     };
-    if (!dlextinfo.library_namespace)
-        return -ENOENT;
-
     void* so = nullptr;
     char prop[PROPERTY_VALUE_MAX];
     for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
@@ -164,7 +171,7 @@
     if (!so)
         return -ENOENT;
 
-    hw_module_t* hmi = static_cast<hw_module_t*>(dlsym(so, HAL_MODULE_INFO_SYM_AS_STR));
+    auto hmi = static_cast<hw_module_t*>(dlsym(so, HAL_MODULE_INFO_SYM_AS_STR));
     if (!hmi) {
         ALOGE("couldn't find symbol '%s' in HAL library: %s", HAL_MODULE_INFO_SYM_AS_STR, dlerror());
         dlclose(so);
@@ -176,11 +183,24 @@
         return -EINVAL;
     }
     hmi->dso = so;
-    *module = hmi;
-    ALOGD("loaded updated driver");
+    *module = reinterpret_cast<const hwvulkan_module_t*>(hmi);
     return 0;
 }
 
+int LoadBuiltinDriver(const hwvulkan_module_t** module) {
+    auto ns = android_get_exported_namespace("sphal");
+    if (!ns)
+        return -ENOENT;
+    return LoadDriver(ns, module);
+}
+
+int LoadUpdatedDriver(const hwvulkan_module_t** module) {
+    auto ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+    if (!ns)
+        return -ENOENT;
+    return LoadDriver(ns, module);
+}
+
 bool Hal::Open() {
     ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
 
@@ -190,9 +210,21 @@
     int result;
     const hwvulkan_module_t* module = nullptr;
 
-    result = LoadUpdatedDriver(reinterpret_cast<const hw_module_t**>(&module));
+    result = LoadUpdatedDriver(&module);
     if (result == -ENOENT) {
-        result = hw_get_module(HWVULKAN_HARDWARE_MODULE_ID, reinterpret_cast<const hw_module_t**>(&module));
+        result = LoadBuiltinDriver(&module);
+        if (result != 0) {
+            // -ENOENT means the sphal namespace doesn't exist, not that there
+            // is a problem with the driver.
+            ALOGW_IF(
+                result != -ENOENT,
+                "Failed to load Vulkan driver into sphal namespace. This "
+                "usually means the driver has forbidden library dependencies."
+                "Please fix, this will soon stop working.");
+            result =
+                hw_get_module(HWVULKAN_HARDWARE_MODULE_ID,
+                              reinterpret_cast<const hw_module_t**>(&module));
+        }
     }
     if (result != 0) {
         ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
@@ -443,6 +475,8 @@
         switch (ext_bit) {
             case ProcHook::KHR_android_surface:
             case ProcHook::KHR_surface:
+            case ProcHook::EXT_swapchain_colorspace:
+            case ProcHook::KHR_get_surface_capabilities2:
                 hook_extensions_.set(ext_bit);
                 // return now as these extensions do not require HAL support
                 return;
@@ -451,6 +485,7 @@
                 hook_extensions_.set(ext_bit);
                 break;
             case ProcHook::EXTENSION_UNKNOWN:
+            case ProcHook::KHR_get_physical_device_properties2:
                 // HAL's extensions
                 break;
             default:
@@ -464,6 +499,15 @@
                 name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
                 ext_bit = ProcHook::ANDROID_native_buffer;
                 break;
+            case ProcHook::KHR_incremental_present:
+            case ProcHook::GOOGLE_display_timing:
+            case ProcHook::KHR_shared_presentable_image:
+                hook_extensions_.set(ext_bit);
+                // return now as these extensions do not require HAL support
+                return;
+            case ProcHook::EXT_hdr_metadata:
+                hook_extensions_.set(ext_bit);
+                break;
             case ProcHook::EXTENSION_UNKNOWN:
                 // HAL's extensions
                 break;
@@ -659,12 +703,21 @@
     const char* pLayerName,
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
-    static const std::array<VkExtensionProperties, 2> loader_extensions = {{
-        // WSI extensions
-        {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION},
-        {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
-         VK_KHR_ANDROID_SURFACE_SPEC_VERSION},
-    }};
+
+    android::Vector<VkExtensionProperties> loader_extensions;
+    loader_extensions.push_back({
+        VK_KHR_SURFACE_EXTENSION_NAME,
+        VK_KHR_SURFACE_SPEC_VERSION});
+    loader_extensions.push_back({
+        VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+        VK_KHR_ANDROID_SURFACE_SPEC_VERSION});
+    loader_extensions.push_back({
+        VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
+        VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION});
+    loader_extensions.push_back({
+        VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
+        VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
+
     static const VkExtensionProperties loader_debug_report_extension = {
         VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION,
     };
@@ -716,32 +769,104 @@
     return result;
 }
 
+bool QueryPresentationProperties(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties)
+{
+    const InstanceData& data = GetData(physicalDevice);
+
+    // GPDP2 must be present and enabled on the instance.
+    if (!data.driver.GetPhysicalDeviceProperties2KHR)
+        return false;
+
+    // Request the android-specific presentation properties via GPDP2
+    VkPhysicalDeviceProperties2KHR properties = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+        presentation_properties,
+        {}
+    };
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+    presentation_properties->sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID;
+#pragma clang diagnostic pop
+    presentation_properties->pNext = nullptr;
+    presentation_properties->sharedImage = VK_FALSE;
+
+    data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
+                                                &properties);
+
+    return true;
+}
+
 VkResult EnumerateDeviceExtensionProperties(
     VkPhysicalDevice physicalDevice,
     const char* pLayerName,
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
     const InstanceData& data = GetData(physicalDevice);
+    // extensions that are unconditionally exposed by the loader
+    android::Vector<VkExtensionProperties> loader_extensions;
+    loader_extensions.push_back({
+        VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+        VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
+
+    VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
+    if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
+        presentation_properties.sharedImage) {
+        loader_extensions.push_back({
+            VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
+            VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
+    }
+
+    // conditionally add VK_GOOGLE_display_timing if present timestamps are
+    // supported by the driver:
+    char timestamp_property[PROPERTY_VALUE_MAX];
+    property_get("service.sf.present_timestamp", timestamp_property, "1");
+    if (strcmp(timestamp_property, "1") == 0) {
+        loader_extensions.push_back({
+                VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
+                VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
+    }
+
+    // enumerate our extensions first
+    if (!pLayerName && pProperties) {
+        uint32_t count = std::min(
+            *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
+
+        std::copy_n(loader_extensions.begin(), count, pProperties);
+
+        if (count < loader_extensions.size()) {
+            *pPropertyCount = count;
+            return VK_INCOMPLETE;
+        }
+
+        pProperties += count;
+        *pPropertyCount -= count;
+    }
 
     VkResult result = data.driver.EnumerateDeviceExtensionProperties(
         physicalDevice, pLayerName, pPropertyCount, pProperties);
-    if (result != VK_SUCCESS && result != VK_INCOMPLETE)
-        return result;
 
-    if (!pProperties)
-        return result;
+    if (pProperties) {
+        // map VK_ANDROID_native_buffer to VK_KHR_swapchain
+        for (uint32_t i = 0; i < *pPropertyCount; i++) {
+            auto& prop = pProperties[i];
 
-    // map VK_ANDROID_native_buffer to VK_KHR_swapchain
-    for (uint32_t i = 0; i < *pPropertyCount; i++) {
-        auto& prop = pProperties[i];
+            if (strcmp(prop.extensionName,
+                       VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+                continue;
 
-        if (strcmp(prop.extensionName,
-                   VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
-            continue;
+            memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+                   sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
+            prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+        }
+    }
 
-        memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
-               sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
-        prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+    // restore loader extension count
+    if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
+        *pPropertyCount += loader_extensions.size();
     }
 
     return result;
@@ -753,6 +878,19 @@
     const VkAllocationCallbacks& data_allocator =
         (pAllocator) ? *pAllocator : GetDefaultAllocator();
 
+    if (pCreateInfo->pApplicationInfo &&
+        pCreateInfo->pApplicationInfo->apiVersion >= VK_MAKE_VERSION(1, 1, 0)) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+        ALOGI(
+            "Requested Vulkan instance version %d.%d is greater than max "
+            "supported version (1.0)",
+            VK_VERSION_MAJOR(pCreateInfo->pApplicationInfo->apiVersion),
+            VK_VERSION_MINOR(pCreateInfo->pApplicationInfo->apiVersion));
+#pragma clang diagnostic pop
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
     CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
     VkResult result = wrapper.Validate();
     if (result != VK_SUCCESS)
@@ -859,7 +997,29 @@
 
         return VK_ERROR_INCOMPATIBLE_DRIVER;
     }
+
+    // sanity check ANDROID_native_buffer implementation, whose set of
+    // entrypoints varies according to the spec version.
+    if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
+        !data->driver.GetSwapchainGrallocUsageANDROID &&
+        !data->driver.GetSwapchainGrallocUsage2ANDROID) {
+        ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
+              " must expose at least one of "
+              "vkGetSwapchainGrallocUsageANDROID or "
+              "vkGetSwapchainGrallocUsage2ANDROID");
+
+        data->driver.DestroyDevice(dev, pAllocator);
+        FreeDeviceData(data, data_allocator);
+
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
+    VkPhysicalDeviceProperties properties;
+    instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+                                                     &properties);
+
     data->driver_device = dev;
+    data->driver_version = properties.driverVersion;
 
     *pDevice = dev;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index a02ebd7..7f8ae98 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -18,8 +18,10 @@
 #define LIBVULKAN_DRIVER_H 1
 
 #include <inttypes.h>
+
 #include <bitset>
 #include <type_traits>
+
 #include <log/log.h>
 
 #include <vulkan/vulkan.h>
@@ -61,7 +63,7 @@
 VK_DEFINE_HANDLE(DeviceDispatchable)
 
 struct InstanceData {
-    InstanceData(const VkAllocationCallbacks& alloc)
+    explicit InstanceData(const VkAllocationCallbacks& alloc)
         : opaque_api_data(),
           allocator(alloc),
           driver(),
@@ -100,12 +102,17 @@
 
     VkDevice driver_device;
     DeviceDriverTable driver;
+    uint32_t driver_version;
 };
 
 bool Debuggable();
 bool OpenHAL();
 const VkAllocationCallbacks& GetDefaultAllocator();
 
+bool QueryPresentationProperties(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+
 // clang-format off
 VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
 VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index d979a34..82b464e 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -17,7 +17,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.
 
 #include <string.h>
+
 #include <algorithm>
+
 #include <log/log.h>
 
 #include "driver.h"
@@ -73,6 +75,41 @@
     }
 }
 
+VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+    if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+        return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
+    } else {
+        Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+    if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+        return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings);
+    } else {
+        Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR void checkedSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata) {
+    if (GetData(device).hook_extensions[ProcHook::EXT_hdr_metadata]) {
+        SetHdrMetadataEXT(device, swapchainCount, pSwapchains, pMetadata);
+    } else {
+        Logger(device).Err(device, "VK_EXT_hdr_metadata not enabled. vkSetHdrMetadataEXT not executed.");
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_shared_presentable_image]) {
+        return GetSwapchainStatusKHR(device, swapchain);
+    } else {
+        Logger(device).Err(device, "VK_KHR_shared_presentable_image not enabled. vkGetSwapchainStatusKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
 // clang-format on
 
 const ProcHook g_proc_hooks[] = {
@@ -218,6 +255,20 @@
         nullptr,
     },
     {
+        "vkGetPastPresentationTimingGOOGLE",
+        ProcHook::DEVICE,
+        ProcHook::GOOGLE_display_timing,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPastPresentationTimingGOOGLE),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
+    },
+    {
+        "vkGetPhysicalDeviceSurfaceCapabilities2KHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_get_surface_capabilities2,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceCapabilities2KHR),
+        nullptr,
+    },
+    {
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
@@ -225,6 +276,13 @@
         nullptr,
     },
     {
+        "vkGetPhysicalDeviceSurfaceFormats2KHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_get_surface_capabilities2,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceFormats2KHR),
+        nullptr,
+    },
+    {
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
@@ -246,6 +304,20 @@
         nullptr,
     },
     {
+        "vkGetRefreshCycleDurationGOOGLE",
+        ProcHook::DEVICE,
+        ProcHook::GOOGLE_display_timing,
+        reinterpret_cast<PFN_vkVoidFunction>(GetRefreshCycleDurationGOOGLE),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetRefreshCycleDurationGOOGLE),
+    },
+    {
+        "vkGetSwapchainGrallocUsage2ANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    {
         "vkGetSwapchainGrallocUsageANDROID",
         ProcHook::DEVICE,
         ProcHook::ANDROID_native_buffer,
@@ -260,6 +332,13 @@
         reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
     },
     {
+        "vkGetSwapchainStatusKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_shared_presentable_image,
+        reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainStatusKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainStatusKHR),
+    },
+    {
         "vkQueuePresentKHR",
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
@@ -273,10 +352,17 @@
         nullptr,
         nullptr,
     },
+    {
+        "vkSetHdrMetadataEXT",
+        ProcHook::DEVICE,
+        ProcHook::EXT_hdr_metadata,
+        reinterpret_cast<PFN_vkVoidFunction>(SetHdrMetadataEXT),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedSetHdrMetadataEXT),
+    },
     // clang-format on
 };
 
-}  // anonymous
+}  // namespace
 
 const ProcHook* GetProcHook(const char* name) {
     const auto& begin = g_proc_hooks;
@@ -292,29 +378,36 @@
     // clang-format off
     if (strcmp(name, "VK_ANDROID_native_buffer") == 0) return ProcHook::ANDROID_native_buffer;
     if (strcmp(name, "VK_EXT_debug_report") == 0) return ProcHook::EXT_debug_report;
+    if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata;
+    if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace;
+    if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
     if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+    if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
+    if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+    if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
+    if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
     // clang-format on
     return ProcHook::EXTENSION_UNKNOWN;
 }
 
 #define UNLIKELY(expr) __builtin_expect((expr), 0)
 
-#define INIT_PROC(obj, proc)                                           \
+#define INIT_PROC(required, obj, proc)                                 \
     do {                                                               \
         data.driver.proc =                                             \
             reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
-        if (UNLIKELY(!data.driver.proc)) {                             \
+        if (UNLIKELY(required && !data.driver.proc)) {                 \
             ALOGE("missing " #obj " proc: vk" #proc);                  \
             success = false;                                           \
         }                                                              \
     } while (0)
 
-#define INIT_PROC_EXT(ext, obj, proc)  \
-    do {                               \
-        if (extensions[ProcHook::ext]) \
-            INIT_PROC(obj, proc);      \
+#define INIT_PROC_EXT(ext, required, obj, proc) \
+    do {                                        \
+        if (extensions[ProcHook::ext])          \
+            INIT_PROC(required, obj, proc);     \
     } while (0)
 
 bool InitDriverTable(VkInstance instance,
@@ -324,14 +417,16 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(instance, DestroyInstance);
-    INIT_PROC(instance, EnumeratePhysicalDevices);
-    INIT_PROC(instance, GetInstanceProcAddr);
-    INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceExtensionProperties);
-    INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
-    INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
-    INIT_PROC_EXT(EXT_debug_report, instance, DebugReportMessageEXT);
+    INIT_PROC(true, instance, DestroyInstance);
+    INIT_PROC(true, instance, EnumeratePhysicalDevices);
+    INIT_PROC(true, instance, GetInstanceProcAddr);
+    INIT_PROC(true, instance, GetPhysicalDeviceProperties);
+    INIT_PROC(true, instance, CreateDevice);
+    INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
     // clang-format on
 
     return success;
@@ -344,15 +439,16 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(dev, GetDeviceProcAddr);
-    INIT_PROC(dev, DestroyDevice);
-    INIT_PROC(dev, GetDeviceQueue);
-    INIT_PROC(dev, CreateImage);
-    INIT_PROC(dev, DestroyImage);
-    INIT_PROC(dev, AllocateCommandBuffers);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, GetSwapchainGrallocUsageANDROID);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, AcquireImageANDROID);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, QueueSignalReleaseImageANDROID);
+    INIT_PROC(true, dev, GetDeviceProcAddr);
+    INIT_PROC(true, dev, DestroyDevice);
+    INIT_PROC(true, dev, GetDeviceQueue);
+    INIT_PROC(true, dev, CreateImage);
+    INIT_PROC(true, dev, DestroyImage);
+    INIT_PROC(true, dev, AllocateCommandBuffers);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
     // clang-format on
 
     return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index a60b2fe..3b26a80 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -19,9 +19,9 @@
 #ifndef LIBVULKAN_DRIVER_GEN_H
 #define LIBVULKAN_DRIVER_GEN_H
 
-#include <bitset>
-#include <vulkan/vulkan.h>
 #include <vulkan/vk_android_native_buffer.h>
+#include <vulkan/vulkan.h>
+#include <bitset>
 
 namespace vulkan {
 namespace driver {
@@ -35,9 +35,16 @@
     enum Extension {
         ANDROID_native_buffer,
         EXT_debug_report,
+        EXT_hdr_metadata,
+        EXT_swapchain_colorspace,
+        GOOGLE_display_timing,
         KHR_android_surface,
+        KHR_incremental_present,
+        KHR_shared_presentable_image,
         KHR_surface,
         KHR_swapchain,
+        KHR_get_surface_capabilities2,
+        KHR_get_physical_device_properties2,
 
         EXTENSION_CORE,  // valid bit
         EXTENSION_COUNT,
@@ -57,11 +64,13 @@
     PFN_vkDestroyInstance DestroyInstance;
     PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
     PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
+    PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
     PFN_vkCreateDevice CreateDevice;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
     PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+    PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
     // clang-format on
 };
 
@@ -74,6 +83,7 @@
     PFN_vkDestroyImage DestroyImage;
     PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
+    PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
     // clang-format on
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 82169ff..05856d3 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -19,14 +19,15 @@
 #include <alloca.h>
 #include <dirent.h>
 #include <dlfcn.h>
-#include <mutex>
-#include <sys/prctl.h>
-#include <string>
 #include <string.h>
+#include <sys/prctl.h>
+
+#include <mutex>
+#include <string>
 #include <vector>
 
-#include <android-base/strings.h>
 #include <android/dlext.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 #include <ziparchive/zip_archive.h>
@@ -68,7 +69,7 @@
 
 class LayerLibrary {
    public:
-    LayerLibrary(const std::string& path)
+    explicit LayerLibrary(const std::string& path)
         : path_(path), dlhandle_(nullptr), refcount_(0) {}
 
     LayerLibrary(LayerLibrary&& other)
diff --git a/vulkan/libvulkan/layers_extensions.h b/vulkan/libvulkan/layers_extensions.h
index 79fe59d..07ac1a3 100644
--- a/vulkan/libvulkan/layers_extensions.h
+++ b/vulkan/libvulkan/layers_extensions.h
@@ -26,7 +26,7 @@
 
 class LayerRef {
    public:
-    LayerRef(const Layer* layer);
+    explicit LayerRef(const Layer* layer);
     LayerRef(LayerRef&& other);
     ~LayerRef();
     LayerRef(const LayerRef&) = delete;
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
new file mode 100644
index 0000000..1745925
--- /dev/null
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -0,0 +1,153 @@
+LIBVULKAN {
+  global:
+    vkAcquireNextImageKHR;
+    vkAllocateCommandBuffers;
+    vkAllocateDescriptorSets;
+    vkAllocateMemory;
+    vkBeginCommandBuffer;
+    vkBindBufferMemory;
+    vkBindImageMemory;
+    vkCmdBeginQuery;
+    vkCmdBeginRenderPass;
+    vkCmdBindDescriptorSets;
+    vkCmdBindIndexBuffer;
+    vkCmdBindPipeline;
+    vkCmdBindVertexBuffers;
+    vkCmdBlitImage;
+    vkCmdClearAttachments;
+    vkCmdClearColorImage;
+    vkCmdClearDepthStencilImage;
+    vkCmdCopyBuffer;
+    vkCmdCopyBufferToImage;
+    vkCmdCopyImage;
+    vkCmdCopyImageToBuffer;
+    vkCmdCopyQueryPoolResults;
+    vkCmdDispatch;
+    vkCmdDispatchIndirect;
+    vkCmdDraw;
+    vkCmdDrawIndexed;
+    vkCmdDrawIndexedIndirect;
+    vkCmdDrawIndirect;
+    vkCmdEndQuery;
+    vkCmdEndRenderPass;
+    vkCmdExecuteCommands;
+    vkCmdFillBuffer;
+    vkCmdNextSubpass;
+    vkCmdPipelineBarrier;
+    vkCmdPushConstants;
+    vkCmdResetEvent;
+    vkCmdResetQueryPool;
+    vkCmdResolveImage;
+    vkCmdSetBlendConstants;
+    vkCmdSetDepthBias;
+    vkCmdSetDepthBounds;
+    vkCmdSetEvent;
+    vkCmdSetLineWidth;
+    vkCmdSetScissor;
+    vkCmdSetStencilCompareMask;
+    vkCmdSetStencilReference;
+    vkCmdSetStencilWriteMask;
+    vkCmdSetViewport;
+    vkCmdUpdateBuffer;
+    vkCmdWaitEvents;
+    vkCmdWriteTimestamp;
+    vkCreateAndroidSurfaceKHR;
+    vkCreateBuffer;
+    vkCreateBufferView;
+    vkCreateCommandPool;
+    vkCreateComputePipelines;
+    vkCreateDescriptorPool;
+    vkCreateDescriptorSetLayout;
+    vkCreateDevice;
+    vkCreateEvent;
+    vkCreateFence;
+    vkCreateFramebuffer;
+    vkCreateGraphicsPipelines;
+    vkCreateImage;
+    vkCreateImageView;
+    vkCreateInstance;
+    vkCreatePipelineCache;
+    vkCreatePipelineLayout;
+    vkCreateQueryPool;
+    vkCreateRenderPass;
+    vkCreateSampler;
+    vkCreateSemaphore;
+    vkCreateShaderModule;
+    vkCreateSwapchainKHR;
+    vkDestroyBuffer;
+    vkDestroyBufferView;
+    vkDestroyCommandPool;
+    vkDestroyDescriptorPool;
+    vkDestroyDescriptorSetLayout;
+    vkDestroyDevice;
+    vkDestroyEvent;
+    vkDestroyFence;
+    vkDestroyFramebuffer;
+    vkDestroyImage;
+    vkDestroyImageView;
+    vkDestroyInstance;
+    vkDestroyPipeline;
+    vkDestroyPipelineCache;
+    vkDestroyPipelineLayout;
+    vkDestroyQueryPool;
+    vkDestroyRenderPass;
+    vkDestroySampler;
+    vkDestroySemaphore;
+    vkDestroyShaderModule;
+    vkDestroySurfaceKHR;
+    vkDestroySwapchainKHR;
+    vkDeviceWaitIdle;
+    vkEndCommandBuffer;
+    vkEnumerateDeviceExtensionProperties;
+    vkEnumerateDeviceLayerProperties;
+    vkEnumerateInstanceExtensionProperties;
+    vkEnumerateInstanceLayerProperties;
+    vkEnumeratePhysicalDevices;
+    vkFlushMappedMemoryRanges;
+    vkFreeCommandBuffers;
+    vkFreeDescriptorSets;
+    vkFreeMemory;
+    vkGetBufferMemoryRequirements;
+    vkGetDeviceMemoryCommitment;
+    vkGetDeviceProcAddr;
+    vkGetDeviceQueue;
+    vkGetEventStatus;
+    vkGetFenceStatus;
+    vkGetImageMemoryRequirements;
+    vkGetImageSparseMemoryRequirements;
+    vkGetImageSubresourceLayout;
+    vkGetInstanceProcAddr;
+    vkGetPhysicalDeviceFeatures;
+    vkGetPhysicalDeviceFormatProperties;
+    vkGetPhysicalDeviceImageFormatProperties;
+    vkGetPhysicalDeviceMemoryProperties;
+    vkGetPhysicalDeviceProperties;
+    vkGetPhysicalDeviceQueueFamilyProperties;
+    vkGetPhysicalDeviceSparseImageFormatProperties;
+    vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+    vkGetPhysicalDeviceSurfaceFormatsKHR;
+    vkGetPhysicalDeviceSurfacePresentModesKHR;
+    vkGetPhysicalDeviceSurfaceSupportKHR;
+    vkGetPipelineCacheData;
+    vkGetQueryPoolResults;
+    vkGetRenderAreaGranularity;
+    vkGetSwapchainImagesKHR;
+    vkInvalidateMappedMemoryRanges;
+    vkMapMemory;
+    vkMergePipelineCaches;
+    vkQueueBindSparse;
+    vkQueuePresentKHR;
+    vkQueueSubmit;
+    vkQueueWaitIdle;
+    vkResetCommandBuffer;
+    vkResetCommandPool;
+    vkResetDescriptorPool;
+    vkResetEvent;
+    vkResetFences;
+    vkSetEvent;
+    vkUnmapMemory;
+    vkUpdateDescriptorSets;
+    vkWaitForFences;
+  local:
+    *;
+};
diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp
index a74d370..2926268 100644
--- a/vulkan/libvulkan/stubhal.cpp
+++ b/vulkan/libvulkan/stubhal.cpp
@@ -29,8 +29,10 @@
 #include <array>
 #include <bitset>
 #include <mutex>
-#include <hardware/hwvulkan.h>
+
 #include <log/log.h>
+#include <hardware/hwvulkan.h>
+
 #include "stubhal.h"
 
 namespace vulkan {
@@ -43,7 +45,7 @@
 static std::bitset<kMaxInstances> g_instance_used(false);
 static std::array<hwvulkan_dispatch_t, kMaxInstances> g_instances;
 
-[[noreturn]] void NoOp() {
+[[noreturn]] VKAPI_ATTR void NoOp() {
     LOG_ALWAYS_FATAL("invalid stub function called");
 }
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 03d37fa..e2c8c06 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,10 +16,12 @@
 
 #include <algorithm>
 
-#include <gui/BufferQueue.h>
+#include <grallocusage/GrallocUsageConversion.h>
 #include <log/log.h>
+#include <ui/BufferQueueDefs.h>
 #include <sync/sync.h>
 #include <utils/StrongPointer.h>
+#include <utils/Vector.h>
 
 #include "driver.h"
 
@@ -105,6 +107,75 @@
     }
 }
 
+class TimingInfo {
+   public:
+    TimingInfo() = default;
+    TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId)
+        : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
+          native_frame_id_(nativeFrameId) {}
+    bool ready() const {
+        return (timestamp_desired_present_time_ !=
+                        NATIVE_WINDOW_TIMESTAMP_PENDING &&
+                timestamp_actual_present_time_ !=
+                        NATIVE_WINDOW_TIMESTAMP_PENDING &&
+                timestamp_render_complete_time_ !=
+                        NATIVE_WINDOW_TIMESTAMP_PENDING &&
+                timestamp_composition_latch_time_ !=
+                        NATIVE_WINDOW_TIMESTAMP_PENDING);
+    }
+    void calculate(int64_t rdur) {
+        bool anyTimestampInvalid =
+                (timestamp_actual_present_time_ ==
+                        NATIVE_WINDOW_TIMESTAMP_INVALID) ||
+                (timestamp_render_complete_time_ ==
+                        NATIVE_WINDOW_TIMESTAMP_INVALID) ||
+                (timestamp_composition_latch_time_ ==
+                        NATIVE_WINDOW_TIMESTAMP_INVALID);
+        if (anyTimestampInvalid) {
+            ALOGE("Unexpectedly received invalid timestamp.");
+            vals_.actualPresentTime = 0;
+            vals_.earliestPresentTime = 0;
+            vals_.presentMargin = 0;
+            return;
+        }
+
+        vals_.actualPresentTime =
+                static_cast<uint64_t>(timestamp_actual_present_time_);
+        int64_t margin = (timestamp_composition_latch_time_ -
+                           timestamp_render_complete_time_);
+        // Calculate vals_.earliestPresentTime, and potentially adjust
+        // vals_.presentMargin.  The initial value of vals_.earliestPresentTime
+        // is vals_.actualPresentTime.  If we can subtract rdur (the duration
+        // of a refresh cycle) from vals_.earliestPresentTime (and also from
+        // vals_.presentMargin) and still leave a positive margin, then we can
+        // report to the application that it could have presented earlier than
+        // it did (per the extension specification).  If for some reason, we
+        // can do this subtraction repeatedly, we do, since
+        // vals_.earliestPresentTime really is supposed to be the "earliest".
+        int64_t early_time = timestamp_actual_present_time_;
+        while ((margin > rdur) &&
+               ((early_time - rdur) > timestamp_composition_latch_time_)) {
+            early_time -= rdur;
+            margin -= rdur;
+        }
+        vals_.earliestPresentTime = static_cast<uint64_t>(early_time);
+        vals_.presentMargin = static_cast<uint64_t>(margin);
+    }
+    void get_values(VkPastPresentationTimingGOOGLE* values) const {
+        *values = vals_;
+    }
+
+   public:
+    VkPastPresentationTimingGOOGLE vals_ { 0, 0, 0, 0, 0 };
+
+    uint64_t native_frame_id_ { 0 };
+    int64_t timestamp_desired_present_time_{ NATIVE_WINDOW_TIMESTAMP_PENDING };
+    int64_t timestamp_actual_present_time_ { NATIVE_WINDOW_TIMESTAMP_PENDING };
+    int64_t timestamp_render_complete_time_ { NATIVE_WINDOW_TIMESTAMP_PENDING };
+    int64_t timestamp_composition_latch_time_
+            { NATIVE_WINDOW_TIMESTAMP_PENDING };
+};
+
 // ----------------------------------------------------------------------------
 
 struct Surface {
@@ -120,12 +191,34 @@
     return reinterpret_cast<Surface*>(handle);
 }
 
+// Maximum number of TimingInfo structs to keep per swapchain:
+enum { MAX_TIMING_INFOS = 10 };
+// Minimum number of frames to look for in the past (so we don't cause
+// syncronous requests to Surface Flinger):
+enum { MIN_NUM_FRAMES_AGO = 5 };
+
 struct Swapchain {
-    Swapchain(Surface& surface_, uint32_t num_images_)
-        : surface(surface_), num_images(num_images_) {}
+    Swapchain(Surface& surface_,
+              uint32_t num_images_,
+              VkPresentModeKHR present_mode)
+        : surface(surface_),
+          num_images(num_images_),
+          mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
+          frame_timestamps_enabled(false),
+          shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+                 present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+        ANativeWindow* window = surface.window.get();
+        native_window_get_refresh_cycle_duration(
+            window,
+            &refresh_duration);
+    }
 
     Surface& surface;
     uint32_t num_images;
+    bool mailbox_mode;
+    bool frame_timestamps_enabled;
+    int64_t refresh_duration;
+    bool shared;
 
     struct Image {
         Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
@@ -137,7 +230,9 @@
         // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
         int dequeue_fence;
         bool dequeued;
-    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
+    } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+    android::Vector<TimingInfo> timing;
 };
 
 VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -205,6 +300,175 @@
             ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
     }
     swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+    swapchain->timing.clear();
+}
+
+uint32_t get_num_ready_timings(Swapchain& swapchain) {
+    if (swapchain.timing.size() < MIN_NUM_FRAMES_AGO) {
+        return 0;
+    }
+
+    uint32_t num_ready = 0;
+    const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1;
+    for (uint32_t i = 0; i < num_timings; i++) {
+        TimingInfo& ti = swapchain.timing.editItemAt(i);
+        if (ti.ready()) {
+            // This TimingInfo is ready to be reported to the user.  Add it
+            // to the num_ready.
+            num_ready++;
+            continue;
+        }
+        // This TimingInfo is not yet ready to be reported to the user,
+        // and so we should look for any available timestamps that
+        // might make it ready.
+        int64_t desired_present_time = 0;
+        int64_t render_complete_time = 0;
+        int64_t composition_latch_time = 0;
+        int64_t actual_present_time = 0;
+        // Obtain timestamps:
+        int ret = native_window_get_frame_timestamps(
+            swapchain.surface.window.get(), ti.native_frame_id_,
+            &desired_present_time, &render_complete_time,
+            &composition_latch_time,
+            NULL,  //&first_composition_start_time,
+            NULL,  //&last_composition_start_time,
+            NULL,  //&composition_finish_time,
+            // TODO(ianelliott): Maybe ask if this one is
+            // supported, at startup time (since it may not be
+            // supported):
+            &actual_present_time,
+            NULL,  //&dequeue_ready_time,
+            NULL /*&reads_done_time*/);
+
+        if (ret != android::NO_ERROR) {
+            continue;
+        }
+
+        // Record the timestamp(s) we received, and then see if this TimingInfo
+        // is ready to be reported to the user:
+        ti.timestamp_desired_present_time_ = desired_present_time;
+        ti.timestamp_actual_present_time_ = actual_present_time;
+        ti.timestamp_render_complete_time_ = render_complete_time;
+        ti.timestamp_composition_latch_time_ = composition_latch_time;
+
+        if (ti.ready()) {
+            // The TimingInfo has received enough timestamps, and should now
+            // use those timestamps to calculate the info that should be
+            // reported to the user:
+            ti.calculate(swapchain.refresh_duration);
+            num_ready++;
+        }
+    }
+    return num_ready;
+}
+
+// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
+void copy_ready_timings(Swapchain& swapchain,
+                        uint32_t* count,
+                        VkPastPresentationTimingGOOGLE* timings) {
+    if (swapchain.timing.empty()) {
+        *count = 0;
+        return;
+    }
+
+    size_t last_ready = swapchain.timing.size() - 1;
+    while (!swapchain.timing[last_ready].ready()) {
+        if (last_ready == 0) {
+            *count = 0;
+            return;
+        }
+        last_ready--;
+    }
+
+    uint32_t num_copied = 0;
+    size_t num_to_remove = 0;
+    for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) {
+        const TimingInfo& ti = swapchain.timing[i];
+        if (ti.ready()) {
+            ti.get_values(&timings[num_copied]);
+            num_copied++;
+        }
+        num_to_remove++;
+    }
+
+    // Discard old frames that aren't ready if newer frames are ready.
+    // We don't expect to get the timing info for those old frames.
+    swapchain.timing.removeItemsAt(0, num_to_remove);
+
+    *count = num_copied;
+}
+
+android_pixel_format GetNativePixelFormat(VkFormat format) {
+    android_pixel_format native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+    switch (format) {
+        case VK_FORMAT_R8G8B8A8_UNORM:
+        case VK_FORMAT_R8G8B8A8_SRGB:
+            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+            break;
+        case VK_FORMAT_R5G6B5_UNORM_PACK16:
+            native_format = HAL_PIXEL_FORMAT_RGB_565;
+            break;
+        case VK_FORMAT_R16G16B16A16_SFLOAT:
+            native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            break;
+        case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+            native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            break;
+        default:
+            ALOGV("unsupported swapchain format %d", format);
+            break;
+    }
+    return native_format;
+}
+
+android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) {
+    switch (colorspace) {
+        case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
+            return HAL_DATASPACE_V0_SRGB;
+        case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
+            return HAL_DATASPACE_DISPLAY_P3;
+        case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
+            return HAL_DATASPACE_V0_SCRGB_LINEAR;
+        case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
+            return HAL_DATASPACE_DCI_P3_LINEAR;
+        case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
+            return HAL_DATASPACE_DCI_P3;
+        case VK_COLOR_SPACE_BT709_LINEAR_EXT:
+            return HAL_DATASPACE_V0_SRGB_LINEAR;
+        case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
+            return HAL_DATASPACE_V0_SRGB;
+        case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
+            return HAL_DATASPACE_BT2020_LINEAR;
+        case VK_COLOR_SPACE_HDR10_ST2084_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
+                HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_DOLBYVISION_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
+                HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_HDR10_HLG_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG |
+                HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_ADOBE_RGB |
+                HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
+            return HAL_DATASPACE_ADOBE_RGB;
+
+        // Pass through is intended to allow app to provide data that is passed
+        // to the display system without modification.
+        case VK_COLOR_SPACE_PASS_THROUGH_EXT:
+            return HAL_DATASPACE_ARBITRARY;
+
+        default:
+            // This indicates that we don't know about the
+            // dataspace specified and we should indicate that
+            // it's unsupported
+            return HAL_DATASPACE_UNKNOWN;
+    }
 }
 
 }  // anonymous namespace
@@ -237,7 +501,7 @@
               err);
         surface->~Surface();
         allocator->pfnFree(allocator->pUserData, surface);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
     }
 
     *out_surface = HandleFromSurface(surface);
@@ -285,13 +549,13 @@
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int transform_hint;
@@ -299,7 +563,7 @@
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // TODO(jessehall): Figure out what the min/max values should be.
@@ -327,7 +591,6 @@
     // TODO(jessehall): I think these are right, but haven't thought hard about
     // it. Do we need to query the driver for support of any of these?
     // Currently not included:
-    // - VK_IMAGE_USAGE_GENERAL: maybe? does this imply cpu mappable?
     // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
     // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
     capabilities->supportedUsageFlags =
@@ -340,10 +603,12 @@
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
-                                            VkSurfaceKHR /*surface*/,
+VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev,
+                                            VkSurfaceKHR surface_handle,
                                             uint32_t* count,
                                             VkSurfaceFormatKHR* formats) {
+    const InstanceData& instance_data = GetData(pdev);
+
     // TODO(jessehall): Fill out the set of supported formats. Longer term, add
     // a new gralloc method to query whether a (format, usage) pair is
     // supported, and check that for each gralloc format that corresponds to a
@@ -356,34 +621,149 @@
         {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
     };
     const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
+    uint32_t total_num_formats = kNumFormats;
+
+    bool wide_color_support = false;
+    Surface& surface = *SurfaceFromHandle(surface_handle);
+    int err = native_window_get_wide_color_support(surface.window.get(),
+                                                   &wide_color_support);
+    if (err) {
+        // Not allowed to return a more sensible error code, so do this
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    ALOGV("wide_color_support is: %d", wide_color_support);
+    wide_color_support =
+        wide_color_support &&
+        instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
+
+    const VkSurfaceFormatKHR kWideColorFormats[] = {
+        {VK_FORMAT_R16G16B16A16_SFLOAT,
+         VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
+        {VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+    };
+    const uint32_t kNumWideColorFormats =
+        sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
+    if (wide_color_support) {
+        total_num_formats += kNumWideColorFormats;
+    }
 
     VkResult result = VK_SUCCESS;
     if (formats) {
-        if (*count < kNumFormats)
+        uint32_t out_count = 0;
+        uint32_t transfer_count = 0;
+        if (*count < total_num_formats)
             result = VK_INCOMPLETE;
-        std::copy(kFormats, kFormats + std::min(*count, kNumFormats), formats);
+        transfer_count = std::min(*count, kNumFormats);
+        std::copy(kFormats, kFormats + transfer_count, formats);
+        out_count += transfer_count;
+        if (wide_color_support) {
+            transfer_count = std::min(*count - out_count, kNumWideColorFormats);
+            std::copy(kWideColorFormats, kWideColorFormats + transfer_count,
+                      formats + out_count);
+            out_count += transfer_count;
+        }
+        *count = out_count;
+    } else {
+        *count = total_num_formats;
     }
-    *count = kNumFormats;
     return result;
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
+VkResult GetPhysicalDeviceSurfaceCapabilities2KHR(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
+    VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
+    VkResult result = GetPhysicalDeviceSurfaceCapabilitiesKHR(
+        physicalDevice, pSurfaceInfo->surface,
+        &pSurfaceCapabilities->surfaceCapabilities);
+
+    VkSurfaceCapabilities2KHR* caps = pSurfaceCapabilities;
+    while (caps->pNext) {
+        caps = reinterpret_cast<VkSurfaceCapabilities2KHR*>(caps->pNext);
+
+        switch (caps->sType) {
+            case VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR: {
+                VkSharedPresentSurfaceCapabilitiesKHR* shared_caps =
+                    reinterpret_cast<VkSharedPresentSurfaceCapabilitiesKHR*>(
+                        caps);
+                // Claim same set of usage flags are supported for
+                // shared present modes as for other modes.
+                shared_caps->sharedPresentSupportedUsageFlags =
+                    pSurfaceCapabilities->surfaceCapabilities
+                        .supportedUsageFlags;
+            } break;
+
+            default:
+                // Ignore all other extension structs
+                break;
+        }
+    }
+
+    return result;
+}
+
+VKAPI_ATTR
+VkResult GetPhysicalDeviceSurfaceFormats2KHR(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
+    uint32_t* pSurfaceFormatCount,
+    VkSurfaceFormat2KHR* pSurfaceFormats) {
+    if (!pSurfaceFormats) {
+        return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
+                                                  pSurfaceInfo->surface,
+                                                  pSurfaceFormatCount, nullptr);
+    } else {
+        // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+        // after the call.
+        android::Vector<VkSurfaceFormatKHR> surface_formats;
+        surface_formats.resize(*pSurfaceFormatCount);
+        VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+            physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+            &surface_formats.editItemAt(0));
+
+        if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
+            // marshal results individually due to stride difference.
+            // completely ignore any chained extension structs.
+            uint32_t formats_to_marshal = *pSurfaceFormatCount;
+            for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+                pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+            }
+        }
+
+        return result;
+    }
+}
+
+VKAPI_ATTR
+VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev,
                                                  VkSurfaceKHR /*surface*/,
                                                  uint32_t* count,
                                                  VkPresentModeKHR* modes) {
-    const VkPresentModeKHR kModes[] = {
-        VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
-    };
-    const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
+    android::Vector<VkPresentModeKHR> present_modes;
+    present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+    present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+
+    VkPhysicalDevicePresentationPropertiesANDROID present_properties;
+    if (QueryPresentationProperties(pdev, &present_properties)) {
+        if (present_properties.sharedImage) {
+            present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+            present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
+        }
+    }
+
+    uint32_t num_modes = uint32_t(present_modes.size());
 
     VkResult result = VK_SUCCESS;
     if (modes) {
-        if (*count < kNumModes)
+        if (*count < num_modes)
             result = VK_INCOMPLETE;
-        std::copy(kModes, kModes + std::min(*count, kNumModes), modes);
+        *count = std::min(*count, num_modes);
+        std::copy(present_modes.begin(), present_modes.begin() + int(*count), modes);
+    } else {
+        *count = num_modes;
     }
-    *count = kNumModes;
     return result;
 }
 
@@ -409,17 +789,28 @@
     if (!allocator)
         allocator = &GetData(device).allocator;
 
+    android_pixel_format native_pixel_format =
+        GetNativePixelFormat(create_info->imageFormat);
+    android_dataspace native_dataspace =
+        GetNativeDataspace(create_info->imageColorSpace);
+    if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
+        ALOGE(
+            "CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
+            "failed: Unsupported color space",
+            create_info->imageColorSpace);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
     ALOGV_IF(create_info->imageArrayLayers != 1,
              "swapchain imageArrayLayers=%u not supported",
              create_info->imageArrayLayers);
-    ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
-             "swapchain imageColorSpace=%u not supported",
-             create_info->imageColorSpace);
     ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
              "swapchain preTransform=%#x not supported",
              create_info->preTransform);
     ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
-               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
+               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ||
+               create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+               create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR),
              "swapchain presentMode=%u not supported",
              create_info->presentMode);
 
@@ -461,51 +852,55 @@
     if (err != 0) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = surface.window->setSwapInterval(surface.window.get(), 1);
+    int swap_interval =
+        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
+    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
+    if (err != 0) {
+        ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_auto_refresh(surface.window.get(), false);
+    if (err != 0) {
+        ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // -- Configure the native window --
 
     const auto& dispatch = GetData(device).driver;
 
-    int native_format = HAL_PIXEL_FORMAT_RGBA_8888;
-    switch (create_info->imageFormat) {
-        case VK_FORMAT_R8G8B8A8_UNORM:
-        case VK_FORMAT_R8G8B8A8_SRGB:
-            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
-            break;
-        case VK_FORMAT_R5G6B5_UNORM_PACK16:
-            native_format = HAL_PIXEL_FORMAT_RGB_565;
-            break;
-        default:
-            ALOGV("unsupported swapchain format %d", create_info->imageFormat);
-            break;
-    }
-    err = native_window_set_buffers_format(surface.window.get(), native_format);
+    err = native_window_set_buffers_format(surface.window.get(),
+                                           native_pixel_format);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
-              native_format, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+              native_pixel_format, strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = native_window_set_buffers_data_space(surface.window.get(),
-                                               HAL_DATASPACE_SRGB_LINEAR);
+                                               native_dataspace);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
-              HAL_DATASPACE_SRGB_LINEAR, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+              native_dataspace, strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_buffers_dimensions(
@@ -517,7 +912,7 @@
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // VkSwapchainCreateInfo::preTransform indicates the transformation the app
@@ -537,7 +932,7 @@
         ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
               InvertTransformToNative(create_info->preTransform),
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_scaling_mode(
@@ -547,7 +942,26 @@
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
+    if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+        create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+        swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
+        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
+        if (err != 0) {
+            ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    }
+
+    if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+        err = native_window_set_auto_refresh(surface.window.get(), true);
+        if (err != 0) {
+            ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
     }
 
     int query_value;
@@ -559,57 +973,53 @@
         // errors and translate them to valid Vulkan result codes?
         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
               query_value);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
-    // The MIN_UNDEQUEUED_BUFFERS query doesn't know whether we'll be using
-    // async mode or not, and assumes not. But in async mode, the BufferQueue
-    // requires an extra undequeued buffer.
-    // See BufferQueueCore::getMinUndequeuedBufferCountLocked().
-    if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
-        min_undequeued_buffers += 1;
-
     uint32_t num_images =
         (create_info->minImageCount - 1) + min_undequeued_buffers;
-    err = native_window_set_buffer_count(surface.window.get(), num_images);
+
+    // Lower layer insists that we have at least two buffers. This is wasteful
+    // and we'd like to relax it in the shared case, but not all the pieces are
+    // in place for that to work yet. Note we only lie to the lower layer-- we
+    // don't want to give the app back a swapchain with extra images (which they
+    // can't actually use!).
+    err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int gralloc_usage = 0;
-    // TODO(jessehall): Remove conditional once all drivers have been updated
-    if (dispatch.GetSwapchainGrallocUsageANDROID) {
+    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+        uint64_t consumer_usage, producer_usage;
+        result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+            device, create_info->imageFormat, create_info->imageUsage,
+            swapchain_image_usage, &consumer_usage, &producer_usage);
+        if (result != VK_SUCCESS) {
+            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        gralloc_usage =
+            android_convertGralloc1To0Usage(producer_usage, consumer_usage);
+    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
         result = dispatch.GetSwapchainGrallocUsageANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             &gralloc_usage);
         if (result != VK_SUCCESS) {
             ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
-            return VK_ERROR_INITIALIZATION_FAILED;
+            return VK_ERROR_SURFACE_LOST_KHR;
         }
-    } else {
-        gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     }
     err = native_window_set_usage(surface.window.get(), gralloc_usage);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    int swap_interval =
-        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
-    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
-        ALOGE("native_window->setSwapInterval(%d) failed: %s (%d)",
-              swap_interval, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // -- Allocate our Swapchain object --
@@ -620,17 +1030,26 @@
                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
     if (!mem)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
-    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
+    Swapchain* swapchain =
+        new (mem) Swapchain(surface, num_images, create_info->presentMode);
 
     // -- Dequeue all buffers and create a VkImage for each --
     // Any failures during or after this must cancel the dequeued buffers.
 
+    VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
+#pragma clang diagnostic pop
+        .pNext = nullptr,
+        .usage = swapchain_image_usage,
+    };
     VkNativeBufferANDROID image_native_buffer = {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wold-style-cast"
         .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
 #pragma clang diagnostic pop
-        .pNext = nullptr,
+        .pNext = &swapchain_image_create,
     };
     VkImageCreateInfo image_create = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -659,7 +1078,7 @@
             // TODO(jessehall): Improve error reporting. Can we enumerate
             // possible errors and translate them to valid Vulkan result codes?
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
-            result = VK_ERROR_INITIALIZATION_FAILED;
+            result = VK_ERROR_SURFACE_LOST_KHR;
             break;
         }
         img.buffer = buffer;
@@ -673,6 +1092,9 @@
         image_native_buffer.stride = img.buffer->stride;
         image_native_buffer.format = img.buffer->format;
         image_native_buffer.usage = img.buffer->usage;
+        android_convertGralloc0To1Usage(img.buffer->usage,
+            &image_native_buffer.usage2.producer,
+            &image_native_buffer.usage2.consumer);
 
         result =
             dispatch.CreateImage(device, &image_create, nullptr, &img.image);
@@ -688,17 +1110,19 @@
     //
     // TODO(jessehall): The error path here is the same as DestroySwapchain,
     // but not the non-error path. Should refactor/unify.
-    for (uint32_t i = 0; i < num_images; i++) {
-        Swapchain::Image& img = swapchain->images[i];
-        if (img.dequeued) {
-            surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
-                                         img.dequeue_fence);
-            img.dequeue_fence = -1;
-            img.dequeued = false;
-        }
-        if (result != VK_SUCCESS) {
-            if (img.image)
-                dispatch.DestroyImage(device, img.image, nullptr);
+    if (!swapchain->shared) {
+        for (uint32_t i = 0; i < num_images; i++) {
+            Swapchain::Image& img = swapchain->images[i];
+            if (img.dequeued) {
+                surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
+                                             img.dequeue_fence);
+                img.dequeue_fence = -1;
+                img.dequeued = false;
+            }
+            if (result != VK_SUCCESS) {
+                if (img.image)
+                    dispatch.DestroyImage(device, img.image, nullptr);
+            }
         }
     }
 
@@ -724,6 +1148,9 @@
     bool active = swapchain->surface.swapchain_handle == swapchain_handle;
     ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
 
+    if (swapchain->frame_timestamps_enabled) {
+        native_window_enable_frame_timestamps(window, false);
+    }
     for (uint32_t i = 0; i < swapchain->num_images; i++)
         ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
     if (active)
@@ -753,8 +1180,10 @@
         }
         for (uint32_t i = 0; i < n; i++)
             images[i] = swapchain.images[i].image;
+        *count = n;
+    } else {
+        *count = swapchain.num_images;
     }
-    *count = swapchain.num_images;
     return result;
 }
 
@@ -777,6 +1206,16 @@
         timeout != UINT64_MAX,
         "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
 
+    if (swapchain.shared) {
+        // In shared mode, we keep the buffer dequeued all the time, so we don't
+        // want to dequeue a buffer here. Instead, just ask the driver to ensure
+        // the semaphore and fence passed to us will be signalled.
+        *image_index = 0;
+        result = GetData(device).driver.AcquireImageANDROID(
+                device, swapchain.images[*image_index].image, -1, semaphore, vk_fence);
+        return result;
+    }
+
     ANativeWindowBuffer* buffer;
     int fence_fd;
     err = window->dequeueBuffer(window, &buffer, &fence_fd);
@@ -784,7 +1223,7 @@
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     uint32_t idx;
@@ -856,17 +1295,56 @@
     ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
              "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
              present_info->sType);
-    ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
 
     VkDevice device = GetData(queue).driver_device;
     const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
 
+    // Look at the pNext chain for supported extension structs:
+    const VkPresentRegionsKHR* present_regions = nullptr;
+    const VkPresentTimesInfoGOOGLE* present_times = nullptr;
+    const VkPresentRegionsKHR* next =
+        reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
+    while (next) {
+        switch (next->sType) {
+            case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
+                present_regions = next;
+                break;
+            case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:
+                present_times =
+                    reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
+                break;
+            default:
+                ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
+                      next->sType);
+                break;
+        }
+        next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext);
+    }
+    ALOGV_IF(
+        present_regions &&
+            present_regions->swapchainCount != present_info->swapchainCount,
+        "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
+    ALOGV_IF(present_times &&
+                 present_times->swapchainCount != present_info->swapchainCount,
+             "VkPresentTimesInfoGOOGLE::swapchainCount != "
+             "VkPresentInfo::swapchainCount");
+    const VkPresentRegionKHR* regions =
+        (present_regions) ? present_regions->pRegions : nullptr;
+    const VkPresentTimeGOOGLE* times =
+        (present_times) ? present_times->pTimes : nullptr;
+    const VkAllocationCallbacks* allocator = &GetData(device).allocator;
+    android_native_rect_t* rects = nullptr;
+    uint32_t nrects = 0;
+
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->pSwapchains[sc]);
         uint32_t image_idx = present_info->pImageIndices[sc];
         Swapchain::Image& img = swapchain.images[image_idx];
+        const VkPresentRegionKHR* region =
+            (regions && !swapchain.mailbox_mode) ? &regions[sc] : nullptr;
+        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
         VkResult swapchain_result = VK_SUCCESS;
         VkResult result;
         int err;
@@ -884,6 +1362,81 @@
             present_info->pSwapchains[sc]) {
             ANativeWindow* window = swapchain.surface.window.get();
             if (swapchain_result == VK_SUCCESS) {
+                if (region) {
+                    // Process the incremental-present hint for this swapchain:
+                    uint32_t rcount = region->rectangleCount;
+                    if (rcount > nrects) {
+                        android_native_rect_t* new_rects =
+                            static_cast<android_native_rect_t*>(
+                                allocator->pfnReallocation(
+                                    allocator->pUserData, rects,
+                                    sizeof(android_native_rect_t) * rcount,
+                                    alignof(android_native_rect_t),
+                                    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+                        if (new_rects) {
+                            rects = new_rects;
+                            nrects = rcount;
+                        } else {
+                            rcount = 0;  // Ignore the hint for this swapchain
+                        }
+                    }
+                    for (uint32_t r = 0; r < rcount; ++r) {
+                        if (region->pRectangles[r].layer > 0) {
+                            ALOGV(
+                                "vkQueuePresentKHR ignoring invalid layer "
+                                "(%u); using layer 0 instead",
+                                region->pRectangles[r].layer);
+                        }
+                        int x = region->pRectangles[r].offset.x;
+                        int y = region->pRectangles[r].offset.y;
+                        int width = static_cast<int>(
+                            region->pRectangles[r].extent.width);
+                        int height = static_cast<int>(
+                            region->pRectangles[r].extent.height);
+                        android_native_rect_t* cur_rect = &rects[r];
+                        cur_rect->left = x;
+                        cur_rect->top = y + height;
+                        cur_rect->right = x + width;
+                        cur_rect->bottom = y;
+                    }
+                    native_window_set_surface_damage(window, rects, rcount);
+                }
+                if (time) {
+                    if (!swapchain.frame_timestamps_enabled) {
+                        ALOGV(
+                            "Calling "
+                            "native_window_enable_frame_timestamps(true)");
+                        native_window_enable_frame_timestamps(window, true);
+                        swapchain.frame_timestamps_enabled = true;
+                    }
+
+                    // Record the nativeFrameId so it can be later correlated to
+                    // this present.
+                    uint64_t nativeFrameId = 0;
+                    err = native_window_get_next_frame_id(
+                            window, &nativeFrameId);
+                    if (err != android::NO_ERROR) {
+                        ALOGE("Failed to get next native frame ID.");
+                    }
+
+                    // Add a new timing record with the user's presentID and
+                    // the nativeFrameId.
+                    swapchain.timing.push_back(TimingInfo(time, nativeFrameId));
+                    while (swapchain.timing.size() > MAX_TIMING_INFOS) {
+                        swapchain.timing.removeAt(0);
+                    }
+                    if (time->desiredPresentTime) {
+                        // Set the desiredPresentTime:
+                        ALOGV(
+                            "Calling "
+                            "native_window_set_buffers_timestamp(%" PRId64 ")",
+                            time->desiredPresentTime);
+                        native_window_set_buffers_timestamp(
+                            window,
+                            static_cast<int64_t>(time->desiredPresentTime));
+                    }
+                }
+
                 err = window->queueBuffer(window, img.buffer.get(), fence);
                 // queueBuffer always closes fence, even on error
                 if (err != 0) {
@@ -898,6 +1451,30 @@
                     img.dequeue_fence = -1;
                 }
                 img.dequeued = false;
+
+                // If the swapchain is in shared mode, immediately dequeue the
+                // buffer so it can be presented again without an intervening
+                // call to AcquireNextImageKHR. We expect to get the same buffer
+                // back from every call to dequeueBuffer in this mode.
+                if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+                    ANativeWindowBuffer* buffer;
+                    int fence_fd;
+                    err = window->dequeueBuffer(window, &buffer, &fence_fd);
+                    if (err != 0) {
+                        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+                        swapchain_result = WorstPresentResult(swapchain_result,
+                            VK_ERROR_SURFACE_LOST_KHR);
+                    }
+                    else if (img.buffer != buffer) {
+                        ALOGE("got wrong image back for shared swapchain");
+                        swapchain_result = WorstPresentResult(swapchain_result,
+                            VK_ERROR_SURFACE_LOST_KHR);
+                    }
+                    else {
+                        img.dequeue_fence = fence_fd;
+                        img.dequeued = true;
+                    }
+                }
             }
             if (swapchain_result != VK_SUCCESS) {
                 ReleaseSwapchainImage(device, window, fence, img);
@@ -914,9 +1491,81 @@
         if (swapchain_result != final_result)
             final_result = WorstPresentResult(final_result, swapchain_result);
     }
+    if (rects) {
+        allocator->pfnFree(allocator->pUserData, rects);
+    }
 
     return final_result;
 }
 
+VKAPI_ATTR
+VkResult GetRefreshCycleDurationGOOGLE(
+    VkDevice,
+    VkSwapchainKHR swapchain_handle,
+    VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    VkResult result = VK_SUCCESS;
+
+    pDisplayTimingProperties->refreshDuration =
+            static_cast<uint64_t>(swapchain.refresh_duration);
+
+    return result;
+}
+
+VKAPI_ATTR
+VkResult GetPastPresentationTimingGOOGLE(
+    VkDevice,
+    VkSwapchainKHR swapchain_handle,
+    uint32_t* count,
+    VkPastPresentationTimingGOOGLE* timings) {
+    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    ANativeWindow* window = swapchain.surface.window.get();
+    VkResult result = VK_SUCCESS;
+
+    if (!swapchain.frame_timestamps_enabled) {
+        ALOGV("Calling native_window_enable_frame_timestamps(true)");
+        native_window_enable_frame_timestamps(window, true);
+        swapchain.frame_timestamps_enabled = true;
+    }
+
+    if (timings) {
+        // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE)
+        copy_ready_timings(swapchain, count, timings);
+    } else {
+        *count = get_num_ready_timings(swapchain);
+    }
+
+    return result;
+}
+
+VKAPI_ATTR
+VkResult GetSwapchainStatusKHR(
+    VkDevice,
+    VkSwapchainKHR swapchain_handle) {
+    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    VkResult result = VK_SUCCESS;
+
+    if (swapchain.surface.swapchain_handle != swapchain_handle) {
+        return VK_ERROR_OUT_OF_DATE_KHR;
+    }
+
+    // TODO(chrisforbes): Implement this function properly
+
+    return result;
+}
+
+VKAPI_ATTR void SetHdrMetadataEXT(
+    VkDevice device,
+    uint32_t swapchainCount,
+    const VkSwapchainKHR* pSwapchains,
+    const VkHdrMetadataEXT* pHdrMetadataEXTs) {
+    // TODO: courtneygo: implement actual function
+    (void)device;
+    (void)swapchainCount;
+    (void)pSwapchains;
+    (void)pHdrMetadataEXTs;
+    return;
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index 2c60c49..e3cf624 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -27,13 +27,19 @@
 VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* allocator);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice pdev, uint32_t queue_family, VkSurfaceKHR surface, VkBool32* pSupported);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkSurfaceFormatKHR* formats);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface_handle, uint32_t* count, VkSurfaceFormatKHR* formats);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkPresentModeKHR* modes);
 VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, const VkAllocationCallbacks* allocator, VkSwapchainKHR* swapchain_handle);
 VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain_handle, const VkAllocationCallbacks* allocator);
 VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
 VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
 VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info);
+VKAPI_ATTR VkResult GetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+VKAPI_ATTR VkResult GetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+VKAPI_ATTR VkResult GetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain);
+VKAPI_ATTR void SetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pHdrMetadataEXTs);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats);
 // clang-format on
 
 }  // namespace driver
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
new file mode 100644
index 0000000..3329609
--- /dev/null
+++ b/vulkan/nulldrv/Android.bp
@@ -0,0 +1,47 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    // Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
+    name: "vulkan.default",
+    proprietary: true,
+    relative_install_path: "hw",
+
+    clang: true,
+    cflags: [
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+        "-DLOG_TAG=\"vknulldrv\"",
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-undef",
+        "-Wno-zero-length-array",
+
+        "-DLOG_NDEBUG=0",
+    ],
+    cppflags: [
+        "-std=c++1y",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-c99-extensions",
+    ],
+
+    srcs: [
+        "null_driver.cpp",
+        "null_driver_gen.cpp",
+    ],
+
+    header_libs: ["vulkan_headers"],
+    shared_libs: ["liblog"],
+}
diff --git a/vulkan/nulldrv/Android.mk b/vulkan/nulldrv/Android.mk
deleted file mode 100644
index 77d4746..0000000
--- a/vulkan/nulldrv/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing \
-	-DLOG_TAG=\"vknulldrv\" \
-	-Weverything -Werror \
-	-Wno-padded \
-	-Wno-undef \
-	-Wno-zero-length-array
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++1y \
-	-Wno-c++98-compat-pedantic \
-	-Wno-c99-extensions
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := \
-	null_driver.cpp \
-	null_driver_gen.cpp
-
-LOCAL_SHARED_LIBRARIES := liblog
-
-# Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
-LOCAL_MODULE := vulkan.default
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3bf3ff7..6714779 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -16,12 +16,13 @@
 
 #include <hardware/hwvulkan.h>
 
-#include <algorithm>
-#include <array>
 #include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
+#include <array>
+
 #include <log/log.h>
 #include <utils/Errors.h>
 
@@ -268,15 +269,8 @@
             layer_name);
     }
 
-// NOTE: Change this to zero to report and extension, which can be useful
-// for testing changes to the loader.
-#if 1
-    (void)properties;  // unused
-    *count = 0;
-    return VK_SUCCESS;
-#else
     const VkExtensionProperties kExtensions[] = {
-        {VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION}};
+        {VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION}};
     const uint32_t kExtensionsCount =
         sizeof(kExtensions) / sizeof(kExtensions[0]);
 
@@ -285,7 +279,6 @@
     if (properties)
         std::copy(kExtensions, kExtensions + *count, properties);
     return *count < kExtensionsCount ? VK_INCOMPLETE : VK_SUCCESS;
-#endif
 }
 
 VKAPI_ATTR
@@ -309,6 +302,10 @@
 
     for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
         if (strcmp(create_info->ppEnabledExtensionNames[i],
+                   VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0) {
+            ALOGV("instance extension '%s' requested",
+                  create_info->ppEnabledExtensionNames[i]);
+        } else if (strcmp(create_info->ppEnabledExtensionNames[i],
                    VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
             ALOGV("instance extension '%s' requested",
                   create_info->ppEnabledExtensionNames[i]);
@@ -513,6 +510,33 @@
     };
 }
 
+void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physical_device,
+                                  VkPhysicalDeviceProperties2KHR* properties) {
+    GetPhysicalDeviceProperties(physical_device, &properties->properties);
+
+    while (properties->pNext) {
+        properties = reinterpret_cast<VkPhysicalDeviceProperties2KHR *>(properties->pNext);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+        switch ((VkFlags)properties->sType) {
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID: {
+            VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties =
+                reinterpret_cast<VkPhysicalDevicePresentationPropertiesANDROID *>(properties);
+#pragma clang diagnostic pop
+
+                // Claim that we do all the right things for the loader to
+                // expose KHR_shared_presentable_image on our behalf.
+                presentation_properties->sharedImage = VK_TRUE;
+            } break;
+
+        default:
+            // Silently ignore other extension query structs
+            break;
+        }
+    }
+}
+
 void GetPhysicalDeviceQueueFamilyProperties(
     VkPhysicalDevice,
     uint32_t* count,
@@ -528,6 +552,12 @@
     }
 }
 
+void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physical_device, uint32_t* count, VkQueueFamilyProperties2KHR* properties) {
+    // note: even though multiple structures, this is safe to forward in this
+    // case since we only expose one queue family.
+    GetPhysicalDeviceQueueFamilyProperties(physical_device, count, properties ? &properties->queueFamilyProperties : nullptr);
+}
+
 void GetPhysicalDeviceMemoryProperties(
     VkPhysicalDevice,
     VkPhysicalDeviceMemoryProperties* properties) {
@@ -543,6 +573,10 @@
     properties->memoryHeaps[0].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
 }
 
+void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physical_device, VkPhysicalDeviceMemoryProperties2KHR* properties) {
+    GetPhysicalDeviceMemoryProperties(physical_device, &properties->memoryProperties);
+}
+
 void GetPhysicalDeviceFeatures(VkPhysicalDevice /*gpu*/,
                                VkPhysicalDeviceFeatures* features) {
     *features = VkPhysicalDeviceFeatures{
@@ -604,6 +638,10 @@
     };
 }
 
+void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physical_device, VkPhysicalDeviceFeatures2KHR* features) {
+    GetPhysicalDeviceFeatures(physical_device, &features->features);
+}
+
 // -----------------------------------------------------------------------------
 // Device
 
@@ -886,6 +924,18 @@
     return VK_SUCCESS;
 }
 
+VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice,
+                                          VkFormat,
+                                          VkImageUsageFlags,
+                                          VkSwapchainImageUsageFlagsANDROID,
+                                          uint64_t* grallocConsumerUsage,
+                                          uint64_t* grallocProducerUsage) {
+    // The null driver never reads or writes the gralloc buffer
+    *grallocConsumerUsage = 0;
+    *grallocProducerUsage = 0;
+    return VK_SUCCESS;
+}
+
 VkResult AcquireImageANDROID(VkDevice,
                              VkImage,
                              int fence,
@@ -1072,11 +1122,22 @@
     ALOGV("TODO: vk%s", __FUNCTION__);
 }
 
+void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
 VkResult GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties) {
     ALOGV("TODO: vk%s", __FUNCTION__);
     return VK_SUCCESS;
 }
 
+VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice,
+                                                    const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo,
+                                                    VkImageFormatProperties2KHR* pImageFormatProperties) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
 VkResult EnumerateInstanceLayerProperties(uint32_t* pCount, VkLayerProperties* pProperties) {
     ALOGV("TODO: vk%s", __FUNCTION__);
     return VK_SUCCESS;
@@ -1129,6 +1190,14 @@
     ALOGV("TODO: vk%s", __FUNCTION__);
 }
 
+void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice,
+                                                      VkPhysicalDeviceSparseImageFormatInfo2KHR const* pInfo,
+                                                      unsigned int* pNumProperties,
+                                                      VkSparseImageFormatProperties2KHR* pProperties) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+
 VkResult QueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence) {
     ALOGV("TODO: vk%s", __FUNCTION__);
     return VK_SUCCESS;
@@ -1334,7 +1403,7 @@
 void CmdCopyImageToBuffer(VkCommandBuffer cmdBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer destBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
 }
 
-void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const void* pData) {
 }
 
 void CmdFillBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize fillSize, uint32_t data) {
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 3a84971..ce15517 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -158,16 +158,7 @@
 }

 PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {«
-    PFN_vkVoidFunction pfn;
-    if ((pfn = Lookup(name, kInstanceProcs)))
-        return pfn;
-    if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID));
-    if (strcmp(name, "vkAcquireImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID));
-    if (strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID));
-    return nullptr;
+    return Lookup(name, kInstanceProcs);
 »}

 } // namespace null_driver
@@ -214,5 +205,6 @@
   {{$ext := index $.Arguments 0}}
   {{     if eq $ext "VK_ANDROID_native_buffer"}}true
   {{else if eq $ext "VK_EXT_debug_report"}}true
+  {{else if eq $ext "VK_KHR_get_physical_device_properties2"}}true
   {{end}}
 {{end}}
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 10da993..25ee65a 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -16,8 +16,8 @@
 
 // WARNING: This file is generated. See ../README.md for instructions.
 
-#include "null_driver_gen.h"
 #include <algorithm>
+#include "null_driver_gen.h"
 
 using namespace null_driver;
 
@@ -54,6 +54,7 @@
 
 const NameProc kInstanceProcs[] = {
     // clang-format off
+    {"vkAcquireImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID))},
     {"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateCommandBuffers>(AllocateCommandBuffers))},
     {"vkAllocateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateDescriptorSets>(AllocateDescriptorSets))},
     {"vkAllocateMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateMemory>(AllocateMemory))},
@@ -170,19 +171,29 @@
     {"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout>(GetImageSubresourceLayout))},
     {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr))},
     {"vkGetPhysicalDeviceFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures>(GetPhysicalDeviceFeatures))},
+    {"vkGetPhysicalDeviceFeatures2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(GetPhysicalDeviceFeatures2KHR))},
     {"vkGetPhysicalDeviceFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties>(GetPhysicalDeviceFormatProperties))},
+    {"vkGetPhysicalDeviceFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties2KHR>(GetPhysicalDeviceFormatProperties2KHR))},
     {"vkGetPhysicalDeviceImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(GetPhysicalDeviceImageFormatProperties))},
+    {"vkGetPhysicalDeviceImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2KHR>(GetPhysicalDeviceImageFormatProperties2KHR))},
     {"vkGetPhysicalDeviceMemoryProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(GetPhysicalDeviceMemoryProperties))},
+    {"vkGetPhysicalDeviceMemoryProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties2KHR>(GetPhysicalDeviceMemoryProperties2KHR))},
     {"vkGetPhysicalDeviceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties>(GetPhysicalDeviceProperties))},
+    {"vkGetPhysicalDeviceProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(GetPhysicalDeviceProperties2KHR))},
     {"vkGetPhysicalDeviceQueueFamilyProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(GetPhysicalDeviceQueueFamilyProperties))},
+    {"vkGetPhysicalDeviceQueueFamilyProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR>(GetPhysicalDeviceQueueFamilyProperties2KHR))},
     {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties))},
+    {"vkGetPhysicalDeviceSparseImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>(GetPhysicalDeviceSparseImageFormatProperties2KHR))},
     {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
     {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+    {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+    {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
     {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
     {"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMergePipelineCaches>(MergePipelineCaches))},
     {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
+    {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
     {"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit>(QueueSubmit))},
     {"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueWaitIdle>(QueueWaitIdle))},
     {"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandBuffer>(ResetCommandBuffer))},
@@ -206,21 +217,7 @@
 }
 
 PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {
-    PFN_vkVoidFunction pfn;
-    if ((pfn = Lookup(name, kInstanceProcs)))
-        return pfn;
-    if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(
-                GetSwapchainGrallocUsageANDROID));
-    if (strcmp(name, "vkAcquireImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID));
-    if (strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkQueueSignalReleaseImageANDROID>(
-                QueueSignalReleaseImageANDROID));
-    return nullptr;
+    return Lookup(name, kInstanceProcs);
 }
 
 }  // namespace null_driver
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 98952b8..8a9a963 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -138,14 +138,14 @@
 VKAPI_ATTR void CmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 VKAPI_ATTR void CmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 VKAPI_ATTR void CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 VKAPI_ATTR void CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 VKAPI_ATTR void CmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
 VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -165,9 +165,20 @@
 VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
 VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
 VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int32_t* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
+VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
+VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
 VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp
new file mode 100644
index 0000000..d81d9ec
--- /dev/null
+++ b/vulkan/tools/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "vkinfo",
+
+    clang: true,
+    cflags: [
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+
+        "-DLOG_TAG=\"vkinfo\"",
+
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-undef",
+        "-Wno-switch-enum",
+    ],
+    cppflags: [
+        "-std=c++1y",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-c99-extensions",
+        "-Wno-old-style-cast",
+    ],
+
+    srcs: ["vkinfo.cpp"],
+
+    shared_libs: [
+        "libvulkan",
+        "liblog",
+    ],
+}
diff --git a/vulkan/tools/Android.mk b/vulkan/tools/Android.mk
deleted file mode 100644
index 337e683..0000000
--- a/vulkan/tools/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing
-LOCAL_CFLAGS += -DLOG_TAG=\"vkinfo\"
-LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef -Wno-switch-enum
-LOCAL_CPPFLAGS := -std=c++1y \
-	-Wno-c++98-compat-pedantic \
-	-Wno-c99-extensions \
-	-Wno-old-style-cast
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := vkinfo.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SHARED_LIBRARIES := libvulkan liblog
-
-LOCAL_MODULE := vkinfo
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 7cf85e6..89bc926 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-#include <algorithm>
-#include <array>
 #include <inttypes.h>
 #include <stdlib.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
 #include <sstream>
 #include <vector>
 
 #include <vulkan/vulkan.h>
 
-#define LOG_TAG "vkinfo"
-#include <log/log.h>
-
 namespace {
 
 struct Options {
@@ -177,7 +176,7 @@
         .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
         .queueFamilyIndex = 0,
         .queueCount = 1,
-        queue_priorities
+        .pQueuePriorities = queue_priorities
     };
     // clang-format off
     const char *kValidationLayers[] = {